您的位置:首页 > 理论基础 > 计算机网络

Unix网络编程之select版客户端实现

2016-09-19 19:37 316 查看
1.当客户端阻塞于某个输入操作时,服务器发送过来的终止连接的消息或者服务器崩溃的FIN,客户端将接受不到,而客户端以为服务器还在正常运行,一味的给服务器发消息。那么客户端将收到一个RST,但是客户端正忙于其他操作,忽略了这个RST,客户端继续调用read方法,如果read方法调用发生在RST到来之前,将返回一个0(EOF),否则返回一个ECONNRESET(对方复位连接错误),最终退出程序。

2.因此,我们可以用select 的i/o复用的方式重新编写我们的代码:

客户端改编之后的代码:

#include "unp.h"
int
main(int argc, char **argv){
int sockfd;
struct sockaddr_in servaddr;
if(argc != 2)
err_quit("usage : tcpcli <IPaddress>");
sockfd = Socket(AF_INET,SOCK_STREAM,0);
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(SERV_PORT);
Inet_pton(AF_INET,argv[i],&servaddr.sin_addr);
Connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr));
strcli(stdin,sockfd);
exit(0);
}


接下来是strcli函数的select 实现方法:

#include "unp.h"
void
str_cli(FILE *fp, int sockfd){
int maxfdp1,stdineof;           //我们设置一个stdineof的标志位,为零代表它可以往sockfd里面写
fd_set rset;
char buf[MAXLINE]
int  n;

stdineof = 0;
FD_ZERO(&rset);
for(;;){
if(stdineof == 0)
FD_SET(fileno(fp),&rset);      //如果标志位为0 , 那么我们把输入流的描述符设置一下。
FD_SET(sockfd,&rset);                //套接字的描述符是都应该设的,因为我们一般只是关闭客户端的写操作,对于服务器发送来的消息还是可以读到的。
maxfdp1 = max(fileno(fp),sockfd)+1;
Select(maxfdp1,&rset,NULL,NULL,NULL);
if(FD_ISSET(sockfd,&rset)){          //判断套接字是否可读,如果可读,读入到buf缓冲区里面。
if((n = Read(sockfd,buf,MAXLINE)) == 0){
if(stdineof == 1){    //如果我们正处在FIN-WAIT-1 状态下,我们就直接返回就行了,因为我们已经受到了来自服务器端的FIN。
return;
}else
err_quit("str_cli : server terminated");
}
Write(fileno(stdout,buf,MAXLINE));
}
if(FD_ISSET(fileno(stdin),&rset)){      //判断输入流里面是否可读,如果可读,把它读到buf缓冲区里面。
if((n = Read(fileno(stdin),buf,MAXLINE)) == 0){

stdineof = 1;              //如果我们读到了来自服务器端的FIN,那么我们就直接将stdineof标志位设置成 1
Shutdown(sockfd,SHUT_WR);   //然后我们向服务器发送ACK,并且还有我们的FIN。
FD_CLR(fileno(fp),&rset);   //接下来我们一定要将rset里面注册的监听描述符移除掉,不让内核再去判断他是否可读
continue;
}
Writen(sockfd,buf,n);
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: