【linux高级程序设计】(第十四章)TCP高级应用

文件I/O方式比较

1.阻塞式文件I/O

进程从调用函数开始,直到返回这段时间都处于阻塞状态。

2.非阻塞式文件I/O

如果当前没有数据可操作,将不阻塞当前进程,而是立即返回一个错误信息。需要反复尝试。

3.多路复用I/O

仍然是阻塞方式等待,但是可以同时等待多个文件描述符。

4.信号驱动I/O

异步方式,等到数据准备好后通知处理进程,不需要重复询问,效率高。

I/O阻塞与非阻塞操作

阻塞方式:默认情况下read/write和 把flag设为0的recv/send

非阻塞方式:如果没有数据,立刻返回-1表示失败,并修改系统全局变量errno的值为EAGAIN,表示数据未准备好。

      通过设置recv的MSG_DONTWAIT标志可以实现。如果设置socket的文件描述符的属性为非阻塞,将导致后续所有针对该文件描述符的操作都为非阻塞。

例子:

服务器端:接收非阻塞,发送阻塞。 可以连续发送多条。如果对方发送的很多数据过来,也会一次性接收。

客户端:发送、接收都阻塞。这个例子里面,接收端一定是收一条、发一条这样交替着的。如果服务器发送了多条,则会分开接收到。

奇怪:这个例子里面,接收端也绑定了自己的地址,而之前的例子里却没有绑定。两者都实现了通信,为什么呢?

服务器代码:

#include<sys/types.h>
#include<sys/socket.h>
#include<stdio.h>
#include<string.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<stdlib.h>
#include<errno.h>
#define BUFSIZE 128

int main(int argc, char *argv[])
{
    int server_sockfd, client_sockfd;
    int server_len, client_len;
    struct sockaddr_in server_address;
    struct sockaddr_in client_address;
    int i, byte;
    char char_send[BUFSIZE];
    //创建socket对象 阻塞
    server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
    server_address.sin_family = AF_INET;
    //从argv[1]提取IP地址
    if(inet_aton(argv[1],(struct in_addr*)&server_address.sin_addr.s_addr) == 0)
    {
        perror(argv[1]);
        exit(EXIT_FAILURE);
    }
    server_address.sin_port = htons(7838);  //使用特定端口
    server_len = sizeof(server_address);
    //绑定IP信息
    bind(server_sockfd, (struct sockaddr *)&server_address, server_len);
    //监听网络
    listen(server_sockfd, 5);
    printf("server waiting for connect\n");
    client_len = sizeof(client_address);
    //等待连接
    client_sockfd = accept(server_sockfd, (struct sockaddr *)&client_address,(socklen_t *)&client_len);
    for(i = 0; i < 5; i++)
    {
        memset(char_send, ‘\0‘, BUFSIZE);
        printf("input message to send:");
        fgets(char_send, BUFSIZE, stdin);  //阻塞在终端,接收用户输入数据
        //发送
        if((byte = send(client_sockfd, char_send, strlen(char_send), 0)) == -1)
        {
            perror("send");
            exit(EXIT_FAILURE);
        }
        memset(char_send, ‘\0‘, BUFSIZE);
        //非阻塞接收
        byte = recv(client_sockfd, char_send, BUFSIZE, MSG_DONTWAIT);
        if(byte > 0)
        {
            printf("get %d message:%s", byte, char_send);
            byte = 0;
        }
        else if(byte < 0)
        {
            if(errno == EAGAIN)
            {
                errno = 0;
                continue;
            }
            else
            {
                perror("recv");
                exit(EXIT_FAILURE);
            }
        }
    }
    //关闭socket对象
    shutdown(client_sockfd, 2);
    shutdown(server_sockfd, 2);
}

客户端代码:

#include<stdio.h>
#include<string.h>
#include<errno.h>
#include<sys/socket.h>
#include<resolv.h>
#include<stdlib.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<fcntl.h>
#define MAXBUF 128
int main(int argc, char **argv)
{
    int sockfd, ret, i;
    struct sockaddr_in dest, mine;
    char buffer[MAXBUF + 1];
    //创建socket对象
    if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        perror("Socket");
        exit(EXIT_FAILURE);
    }
    bzero(&dest, sizeof(dest));
    dest.sin_family = AF_INET;
    dest.sin_port = htons(7838);  //服务器的特定端口,与服务器端设置一致
    //获取服务器IP地址,由argv[1]指定
    if(inet_aton(argv[1], (struct in_addr *)&dest.sin_addr.s_addr) == 0)
    {
        perror(argv[1]);
        exit(EXIT_FAILURE);
    }
    bzero(&mine, sizeof(mine));
    mine.sin_family = AF_INET;
    mine.sin_port = htons(7839);   //本地端口
    //本地IP地址,由argv[2]指定
    if(inet_aton(argv[2], (struct in_addr *)&mine.sin_addr.s_addr) == 0)
    {
        perror(argv[2]);
        exit(EXIT_FAILURE);
    }
    //绑定自己的IP地址信息
    if(bind(sockfd, (struct sockaddr *)&mine, sizeof(struct sockaddr)) == -1)
    {
        perror("bind");
        exit(EXIT_FAILURE);
    }
    //发起连接
    if(connect(sockfd, (struct sockaddr *)&dest, sizeof(dest)) != 0)
    {
        perror("Connect");
        exit(EXIT_FAILURE);
    }
    //设置sockfd描述符为非阻塞
    if(fcntl(sockfd, F_SETFL, O_NONBLOCK) == -1)
    {
        perror("fcntl");
        exit(EXIT_FAILURE);
    }
    while(1)
    {
        bzero(buffer, MAXBUF + 1);
        //接收
        ret = recv(sockfd, buffer, MAXBUF, 0); //因为设置socket非阻塞,故此操作非阻塞
        if(ret > 0)
        {
            printf("get %d message:%s", ret, buffer);
            ret = 0;
        }
        else if(ret < 0)
        {
            if(errno == EAGAIN)
            {
                errno = 0;
                continue;
            }
            else
            {
                perror("recv");
                exit(EXIT_FAILURE);
            }
        }
        memset(buffer, ‘\0‘, MAXBUF + 1);
        printf("input message to send:");
        fgets(buffer, MAXBUF, stdin);  //在接收到数据后阻塞在终端,向对方发
        if((ret = send(sockfd, buffer, strlen(buffer), 0)) == -1) //发送数据
        {
            perror("send");
            exit(EXIT_FAILURE);
        }
    }
    close(sockfd);
    return 0;
}

我在同一台虚拟机的不同终端做实验

服务器端结果:

客户端结果:

时间: 2024-12-19 21:53:29

【linux高级程序设计】(第十四章)TCP高级应用的相关文章

javascript高级程序设计 第十四章--表单脚本

javascript高级程序设计 第十四章--表单脚本 在HTML中表单由<form>元素表示,在js中表单对应的是HTMLFormElement类型,这个类型也有很多属性和方法:取得表单元素的引用还是为它添加id特性,用DOM操作来获取表单元素:提交表单:把<input>或<button>元素的type特性设置为"submit",图像按钮把<input>元素的type特性设置为"image",也可以调用submit(

读书笔记 - js高级程序设计 - 第十五章 使用Canvas绘图

读书笔记 - js高级程序设计 - 第十三章 事件 canvas 具备绘图能力的2D上下文 及文本API 很多浏览器对WebGL的3D上下文支持还不够好 有时候即使浏览器支持,操作系统如果缺缺乏必要的绘图驱动程序,则浏览器即使支持了也没用   <canvas> var drawing = document.getElementById("drawing"); if( drawing.getContext ){ drawing.getContext("2d"

【读书笔记】C#高级编程 第二十四章 文件和注册表操作

(一)文件和注册表 对于文件系统操作,相关的类几乎都在System.IO名称空间中,而注册表操作由System.Win32名称空间中的类来处理. (二)管理文件系统 System.MarshalByRefObject--这是.NET类中用于远程操作的基对象类,它允许在应用程序域之间编组数据. FileSystemInfo--这是表示任何文件系统对象的基类. FileInfo和File--这些类表示文件系统上的文件. DirectoryInfo和Directory--这些类表示文件系统上的文件夹.

linux学习笔记-第二十四课-LNMP-Nginx高级配置(三)

一.用户认证 用户认证功能是利用Apache的工具htpasswd生成的密钥,所以需要安装Apache的这个工具即可,我们用yum来安装就可以. [[email protected] ~]# yum install -y httpd-tools [[email protected] ~]# htpasswd -cm /usr/local/nginx/conf/.htpasswd mydiscuz New password: Re-type new password: Adding passwor

鸟0哥的Linux私房菜——第十四章:Bash Shell

视频链接 土豆: B站: 本章目录: 1. Bash shell1.1 什么是 shell ? (我们通过shell与Kernel核心沟通,使Kernel操控硬件)1.2 系统的 shell 与 /etc/shells 功能 (cat /etc/shells一下,可以看到当前电脑的shell)1.3 Bash shell 的功能  (1.命令编修能力,就是记忆你写的命令按上下键切换.2.命令与档案补全功能,就是tab键补全.3.命令别名(alias)设定功能alias命令.下面还有一些介绍)1.

【读书笔记】C#高级编程 第十四章 内存管理和指针

(一)后台内存管理 1.值数据类型 Windows使用一个虚拟寻址系统,该系统把程序可用的内存地址映射到硬件内存中的实际地址,该任务由Windows在后台管理(32位每个进程可使用4GB虚拟内存,64位更多,这个内存包括可执行代码和加载的DLL,以及程序运行时使用的变量内容). 在处理器的虚拟内存中,有一个区域称为栈.栈存储不是对象成员的值数据类型. 释放变量时,其顺序总是与它们分配内存的顺序相反,这就是栈的工作方式. 程序第一次运行时,栈指针指向为栈保留的内存块末尾.栈实际上是向下填充的,即从

JavaScript高级程序设计:第四章

变量.作用域和内存问题 1.ECMAScript变量可能包含两种不同数据类型的值:基本类型值和引用类型值.基本类型值指的是简单的数据段,引用类型值指的是有多个值构成的对象. 2.动态的属性:定义一个基本类型值和引用类型值的方法是类似的:创建一个变量并为该变量赋值.但是,当这个值保存到变量中以后,对不同类型值可以执行的操作大相庭径.对于引用类型的值,我们可以为其添加属性和方法,也可以改变和删除其属性和方法.如下例子: var  person = new Object(): person.name

《JAVASCRIPT高级程序设计》第四章

javascript变量是松散类型,它只是在特定时间表示特定值的一个名字而已:变量的值以及类型,可以在脚本的生命周期内改变.变量的类型,分为基本类型和引用类型两种,具体介绍如下图所示: 执行环境是JavaScript中的一个重要概念,它定义了变量或函数是否有权访问其他的数据,决定了它们各自的行为.每个执行环境都有一个与之关联的变量对象,环境中定义的所有变量和函数都保存在这个对象中.虽然我们编写的代码无法访问这个对象,但解析器在处理数据时,会在后台用到它.

读书笔记 - js高级程序设计 - 第十二章 DOM2和DOM3

Node类型的变化   访问元素的样式 myDiv.style.backgroundColor = "red" myDiv.style.width = "100px" 计算的样式 记住所有计算的样式都是只读的 偏移量 offsetHeight 外边框外 offsetWidth offsetLeft  外边框外 到 左端 offsetTop  客户区的大小 clientWidth  内边框外缘 clientHeight 内边框外缘 滚动大小 scrollHeight

《JavaScript 高级程序设计》第四章:变量、作用域和内存问题

目录 变量的引用 执行环境及作用域 作用域链延长 块级作用域 垃圾回收机制 变量的引用 当一个变量保存了基本数据类型时,此时对于变量的操作(赋值,运算)就是操作这个基本数据的本身,就算是赋值操作,赋值时拷贝后的值与之前的值也是相互独立互不影响的. var a = 1; var b = a b++; console.log(a); //1 console.log(b); //2 这非常好理解,但是如果一个变量保存的是一个引用类型的数据,例如对象,那么情况将会不同,这是因为变量保存的并不是对象本身,