为什么内核在发送一定数量的字节后强制从客户端发送 TCP RST?

问题描述

我有简单的“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) 的虚拟解决方案。之后它起作用了。