select与shutdown

来源:互联网 发布:阿里云centos7 lamp 编辑:程序博客网 时间:2024/05/22 03:04

相关网络编程函数:http://blog.csdn.net/somehow1002/article/details/72648743

我们已经实现了一个基本的并发回射服务器程序,但是依然存在问题。

如果当客户端阻塞于标准输入时,服务器进程可能因为各种原因结束了。服务器TCP虽然正确的给客户端TCP发送了一个FIN,但是客户端进程正阻塞在标准输入上,可能很长时间之后才能接受到这个信息。


针对这个问题,可以使用IO复用方法来解决。
客户端程序既要处理标准输入,又要处理套结字上到达的信息。对于多种IO,使用select函数解决这个问题。
客户端要处理的各种信息如下:

对于客户端的套结字上的三个条件处理如下:
1.如果对端TCP发送数据,那么该套结字变为可读,并且返回一个大于0的值
2.如果对端TCP发送了一个FIN(对端进程终止),那么该套结字变为可读,并且read返回0(EOF)
3.如果对端TCP发送了一个RST(对端主机崩溃并重新启动),那么该套结字变为可读,并且read返回-1,而errno中含有确切的错误码。


依此我们修订str_cli函数如下

void str_cli(FILE *fp,int sockfd){    int maxfdp1;    fdset rset;    char sendline[MAXLINE],recvline[MAXLINE];        FD_ZERO(&rset);    for(;;){                FD_SET(fileno(fp),&rset);        FD_SET(sockfd,$rset);        maxfdp1=max(fileno(fp),sockfd)+1;        select(maxfdp1,&rset,NULL,NULL,NULL);                if(FD_ISSET(sockfd,&rset)){            if(readline(sockfd,recvline,MAXLINE)==0){                printf("str_cli:server terminated prematurely");                exit(-1);            }            fputs(recvline,stdout);        }                if(FD_ISSET(fileno(fp),&rset)){            if(fgets(sendline,MAXLINE,fp)==NULL)                return ;            Writen(sockfd,sendline,strlen(sendline));        }    }}

问题
设想一种情况,客户端要传输大量的消息,这些消息正在传输中。而我们写完所有请求之后,并不能立即关闭连接,因为管道中还有其他的请求和应答。问题的起因在于我们对标准输入中EOF的处理:str_cli函数就此返回到main函数,而main函数随后终止。然而在这种批量方式下,标准输入中的EOF并不意味这完成了从套结字的读入。总结起来就是:客户端输入的EOF并不意味这客户端处理的完成(可能还要从套接字读入,然而客户端已经结束了)。


要解决这个问题,我们将套结字的读写两个方向的数据传输分开处理,使用shutdown函数,而不是原先的close函数。


使用select,shutdown正确处理EOF的str_cli函数

void str_cli(FILE *fp,int sockfd){    int maxfdp1,stdineof;    fd_set rset;    char buf[MAXLINE];    int n;        stdineof=0;    FD_ZERO(&rset);    for(;;){        if(stdineof==0)            FD_SET(fileno(fp),&rset);        FD_SET(sockfd,&rset);        maxfdp1=max(fileno(fp),sockfd)+1;        select(maxfdp1,&rset,NULL,NULL,NULL);                if(FD_ISSET(sockfd,&rset)){            if((n=read(sockfd,buf,MAXLINE))==0){                if(stdineof==1)                    return ;                else                {                    printf("str_cli:server terminated prematurely\n");                    exit(-1);                }            }            write(fileno(stdout),buf,n);        }                if(FD_ISSET(fileno(fp),&rset)){            if((n=read(fileno(fp),buf,MAXLINE))==0){                stdineof=1;                shutdown(sockfd,SHUT_WR);                FD_CLR(fileno(fp),&rset);                continue;            }            Writen(sockfd,buf,n);        }    }}