增加I/O多路复用的回射程序

来源:互联网 发布:消防三知四会四个能力 编辑:程序博客网 时间:2024/04/28 05:22

在上一版本的回射程序中,若服务器子进程被杀死,则客户端检测不到这一事件的发生。原因在于,子进程被杀死时,虽然发送了FIN给客户端套接字,但此时客户端进程是阻塞于等待标准输入上的,因此检测不到套接字的输入。解决办法就是使用I/O多路复用。如下图所示:



另一个问题:若输入是批量输入,则输入结束后,客户端检测到EOF,则客户端会关闭连接,而网络中还有其他请求和应答,则会发现输出比输入少的现象,如下图所示


当全部的9个请求发出后,则会关闭连接,而此时只有应答1被接收到,其他的会丢失。

解决办法就是请求全部发送后,只关闭套接字写的那端,不关闭读的那端,也即半关闭状态

为此,使用了select来进行I/O多路复用。程序如下:

客户端:

#include <stdio.h>#include <sys/socket.h>#include <netinet/in.h>#include <string.h>#include <stdlib.h>#include <sys/select.h>#include <sys/time.h>#define err_exit(m)\        {\                perror(m);\                exit(EXIT_FAILURE);\        }#define SERV_PORT 9877#define BUFSIZE 4096int max(int a, int b){        if (a > b)                return a;        else                return b;}//客户端具体操作函数void str_cli(FILE *fp, int sockfd);int main(int argc, char **argv){        if (argc != 2)        {                printf("argument error\n");                exit(0);        }        int sockfd;        struct sockaddr_in servaddr;        int status;        bzero(&servaddr, sizeof(servaddr));        servaddr.sin_family = AF_INET;        servaddr.sin_port = htons(SERV_PORT);        inet_pton(AF_INET, argv[1], &servaddr.sin_addr);//将点分十进制IP地址转化为网络字节序的二进制地址        sockfd = socket(AF_INET, SOCK_STREAM, 0);        if (sockfd == -1)                err_exit("socket");        status = connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));//连接服务器        if (status == -1)                err_exit("connect");        str_cli(stdin, sockfd);        exit(0);}void str_cli(FILE *fp, int sockfd){        int stdineof = 0;        int maxfdp1;        fd_set set;        int n;        char buf[BUFSIZE];        FD_ZERO(&set);        for (;;)        {                if (stdineof == 0)                        FD_SET(fileno(fp), &set);//若stdineof = 1, 代表输入已完成,不再监听该描述符                FD_SET(sockfd, &set);                maxfdp1 = max(fileno(fp), sockfd) + 1;                select(maxfdp1, &set, NULL, NULL, NULL);//使用select的I/O多路复用                if (FD_ISSET(sockfd, &set))//套接字可读                {                        if ((n = read(sockfd, buf, BUFSIZE)) == 0)                        {                                if (stdineof == 1)                                        return ; //正常退出                                else                                {                                        printf("server terminated prematurely");                                        exit(1);                                }                        }                        write(fileno(stdout), buf, n);                }                if (FD_ISSET(fileno(fp), &set))//标准输入可读                {                        if ((n = read(fileno(fp), buf, BUFSIZE)) == 0) //为0,则表示输入结束                        {                                stdineof = 1;                                shutdown(sockfd, SHUT_WR);                                FD_CLR(fileno(fp), &set);                                continue;                        }                        write(sockfd, buf, n);                }        }}


0 0
原创粉丝点击