• 【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
    ");
        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, '', 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, '', 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, '', 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;
    }

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

    服务器端结果:

    客户端结果:

    分析一下这个代码:

    服务器端

    for循环只有5次,且发送数据阻塞,意味着一定是发送一次消息后,i才会加1,故一共能发送5条消息。

    接收是非阻塞,且在发送后面。即如果有客户端发来的的信息,在服务器发送消息后,能够接收到客户端信息。但是如果没有,服务器端直接continue。即接收的消息会少于5条。

    其实非阻塞的意思就是代码直接返回错误,具体后面怎么处理,是我们自己写代码决定的。

    客户端

    接收数据非阻塞,但是接收数据在发送数据前面,且接收数据失败后会直接continue, 这导致如果接收数据失败则在当前循环下无法发送数据。故客户端的效果一定是接收一条,发送一条这样交替的。

    发送数据非阻塞,但是因为fgets会阻塞在终端,所以看不出发送的非阻塞效果。

  • 相关阅读:
    QDUOJ 来自xjy的签到题(bfs+状压dp)
    HDU
    【原创+整理】线程同步之详解自旋锁
    【原创】浅说windows下的中断请求级IRQL
    【原创】驱动开发中Memory read error导致的蓝屏问题
    [转&精]IO_STACK_LOCATION与IRP的一点笔记
    【原创】《windows驱动开发技术详解》第4章实验总结二
    【原创】《windows驱动开发技术详解》第4章实验总结一
    【转载】LINUX 和 WINDOWS 内核的区别
    【原创】Windows服务管家婆之Service Control Manager
  • 原文地址:https://www.cnblogs.com/dplearning/p/4700621.html
Copyright © 2020-2023  润新知