zoukankan      html  css  js  c++  java
  • 【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会阻塞在终端,所以看不出发送的非阻塞效果。

  • 相关阅读:
    MKMapVIew学习系列2 在地图上绘制出你运行的轨迹
    WPF SDK研究 Intro(6) WordGame1
    WPF SDK研究 Intro(3) QuickStart3
    WPF SDK研究 Layout(1) Grid
    WPF SDK研究 目录 前言
    WPF SDK研究 Intro(7) WordGame2
    WPF SDK研究 Layout(2) GridComplex
    对vs2005创建的WPF模板分析
    WPF SDK研究 Intro(4) QuickStart4
    《Programming WPF》翻译 第6章 资源
  • 原文地址:https://www.cnblogs.com/dplearning/p/4700621.html
Copyright © 2011-2022 走看看