问题描述
我有简单的“echo”客户端-服务器代码,并使用 tcpdump
观看 tcp 流,客户端总是在发送 274
后发送 RST。总是。但我不知道如何追踪这个问题。客户:
//FILE to read from (stdin),sockfd from connect()
void str_cli(FILE *fp,int sockfd)
{
int maxfd;
fd_set rset;
//BSIZE == 4096
char recvline[BSIZE],sendline[BSIZE];
int streamfd = fileno(fp);
//clear the read set
FD_ZERO(&rset);
while (1)
{
FD_SET(streamfd,&rset);
FD_SET(sockfd,&rset);
maxfd = max(streamfd,sockfd) + 1;
//select blocks until one of the fds are readable
if (select(maxfd,&rset,NULL,NULL) < 0)
{
die("select");
}
if (FD_ISSET(sockfd,&rset))
{
//socket is readable
if (Readline(sockfd,recvline,BSIZE) == 0)
{
die("str_cli: server terminated prematurely");
}
Fputs(recvline,stdout);
}
if (FD_ISSET(streamfd,&rset))
{
//got input and can read from streamfd
if (fgets(sendline,BSIZE,fp) == NULL)
{
//EOF == all done
perror("fgets in select");
return;
}
Writen(sockfd,sendline,strlen(sendline));
}
}
}
服务器:
//sockfd is socket returned from accept()
void str_echo(int sockfd)
{
ssize_t len;
//BSIZE == 4096
char buf[BSIZE];
again:
while ((len = read(sockfd,buf,BSIZE)) > 0)
{
Writen(sockfd,len);
}
if (len < 0 && errno == EINTR)
{
goto again;
}
else if (len < 0)
{
perror("str_echo::read");
}
}
我将 BSIZE 设置为 256,我认为内核因为缓冲区溢出而杀死了客户端,所以我将其更改为 4096。但问题仍然存在,在客户端发送 274
字节之后,它发送 RST
,我不知道为什么。最后的转储
127.0.0.1:9877 -> 服务器 127.0.0.1:46790 -> 客户端
tcpdump: verbose output suppressed,use -v or -vv for full protocol decode
listening on any,link-type LINUX_sll (Linux cooked v1),capture size 262144 bytes
IP 127.0.0.1.46790 > 127.0.0.1.9877: Flags [S],seq 1941257529,win 65495,options [mss 65495,sackOK,TS val 2375820429 ecr 0,nop,wscale 7],length 0
IP 127.0.0.1.9877 > 127.0.0.1.46790: Flags [S.],seq 4099864764,ack 1941257530,win 65483,TS val 2375820429 ecr 2375820429,length 0
IP 127.0.0.1.46790 > 127.0.0.1.9877: Flags [.],ack 1,win 512,options [nop,TS val 2375820429 ecr 2375820429],length 0
IP 127.0.0.1.46790 > 127.0.0.1.9877: Flags [P.],seq 1:37,TS val 2375820430 ecr 2375820429],length 36
IP 127.0.0.1.9877 > 127.0.0.1.46790: Flags [.],ack 37,TS val 2375820430 ecr 2375820430],seq 37:81,length 44
IP 127.0.0.1.9877 > 127.0.0.1.46790: Flags [.],ack 81,seq 81:82,length 1
IP 127.0.0.1.9877 > 127.0.0.1.46790: Flags [.],ack 82,seq 82:92,length 10
IP 127.0.0.1.9877 > 127.0.0.1.46790: Flags [.],ack 92,length 0
IP 127.0.0.1.9877 > 127.0.0.1.46790: Flags [P.],seq 1:82,length 81
IP 127.0.0.1.9877 > 127.0.0.1.46790: Flags [.],ack 194,seq 194:210,length 16
IP 127.0.0.1.46790 > 127.0.0.1.9877: Flags [P.],seq 210:226,seq 226:236,ack 266,seq 82:274,ack 274,length 192
IP 127.0.0.1.46790 > 127.0.0.1.9877: Flags [R.],seq 274,length 0
19 packets captured
56 packets received by filter
16 packets dropped by kernel
有谁知道,为什么内核会强制终止客户端? (通过发送 RST)?
编辑:
如果我使用 netcat 而不是客户端 nc 127.0.0.1 9877 < somefile
,服务器会正确响应它,所以我怀疑客户端在某处有问题。但是内核还是强行让客户端发送RST,但是为什么呢?
解决方法
问题是,在客户端发送完所有字节后,它不会等待服务器回复并关闭所有套接字(进程退出(0),但是当进程退出时,内核关闭所有打开的文件描述符)。所以数据在一端发送,但套接字在另一端可以回显之前关闭。所以因为接收套接字的缓冲区不是空的,所以回复是RST
。从客户端发送字节后,我制作了 sleep(1)
的虚拟解决方案。之后它起作用了。