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

本文探讨了在Unix/Linux环境下,使用select进行I/O复用的TCP客户端实现。当客户端使用单进程模式时,可能会遇到服务器关闭连接但客户端无法感知的问题,导致客户端在后续操作中收到RST或EOF。为解决这个问题,文章提供了使用select进行改进的客户端代码,以确保客户端能够正确处理服务器的终止连接或异常情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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);	
	  }
      }
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值