【问题标题】:Why kernel forcefully sends TCP RST from client after certain amount of bytes send?为什么内核在发送一定数量的字节后强制从客户端发送 TCP RST?
【发布时间】:2021-01-11 01:38:39
【问题描述】:

我有简单的“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, 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, buf, len);
    }
    if (len < 0 && errno == EINTR)
    {
        goto again;
    }
    else if (len < 0)
    {
        perror("str_echo::read");
    }
}

我将 BSIZE 设置为 256,我认为内核由于缓冲区溢出而正在杀死客户端,所以我将其更改为 4096。但问题仍然存在,客户端发送 274 字节后,它发送 @ 987654326@ 我不知道为什么。最后的转储

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, options [mss 65495,sackOK,TS val 2375820429 ecr 2375820429,nop,wscale 7], length 0
IP 127.0.0.1.46790 > 127.0.0.1.9877: Flags [.], ack 1, win 512, options [nop,nop,TS val 2375820429 ecr 2375820429], length 0
IP 127.0.0.1.46790 > 127.0.0.1.9877: Flags [P.], seq 1:37, ack 1, win 512, options [nop,nop,TS val 2375820430 ecr 2375820429], length 36
IP 127.0.0.1.9877 > 127.0.0.1.46790: Flags [.], ack 37, win 512, options [nop,nop,TS val 2375820430 ecr 2375820430], length 0
IP 127.0.0.1.46790 > 127.0.0.1.9877: Flags [P.], seq 37:81, ack 1, win 512, options [nop,nop,TS val 2375820430 ecr 2375820430], length 44
IP 127.0.0.1.9877 > 127.0.0.1.46790: Flags [.], ack 81, win 512, options [nop,nop,TS val 2375820430 ecr 2375820430], length 0
IP 127.0.0.1.46790 > 127.0.0.1.9877: Flags [P.], seq 81:82, ack 1, win 512, options [nop,nop,TS val 2375820430 ecr 2375820430], length 1
IP 127.0.0.1.9877 > 127.0.0.1.46790: Flags [.], ack 82, win 512, options [nop,nop,TS val 2375820430 ecr 2375820430], length 0
IP 127.0.0.1.46790 > 127.0.0.1.9877: Flags [P.], seq 82:92, ack 1, win 512, options [nop,nop,TS val 2375820430 ecr 2375820430], length 10
IP 127.0.0.1.9877 > 127.0.0.1.46790: Flags [.], ack 92, win 512, options [nop,nop,TS val 2375820430 ecr 2375820430], length 0
IP 127.0.0.1.9877 > 127.0.0.1.46790: Flags [P.], seq 1:82, ack 92, win 512, options [nop,nop,TS val 2375820430 ecr 2375820430], length 81
IP 127.0.0.1.9877 > 127.0.0.1.46790: Flags [.], ack 194, win 512, options [nop,nop,TS val 2375820430 ecr 2375820430], length 0
IP 127.0.0.1.46790 > 127.0.0.1.9877: Flags [P.], seq 194:210, ack 82, win 512, options [nop,nop,TS val 2375820430 ecr 2375820430], length 16
IP 127.0.0.1.46790 > 127.0.0.1.9877: Flags [P.], seq 210:226, ack 82, win 512, options [nop,nop,TS val 2375820430 ecr 2375820430], length 16
IP 127.0.0.1.46790 > 127.0.0.1.9877: Flags [P.], seq 226:236, ack 82, win 512, options [nop,nop,TS val 2375820430 ecr 2375820430], length 10
IP 127.0.0.1.9877 > 127.0.0.1.46790: Flags [.], ack 266, win 512, options [nop,nop,TS val 2375820430 ecr 2375820430], length 0
IP 127.0.0.1.9877 > 127.0.0.1.46790: Flags [P.], seq 82:274, ack 274, win 512, options [nop,nop,TS val 2375820430 ecr 2375820430], length 192
IP 127.0.0.1.46790 > 127.0.0.1.9877: Flags [R.], seq 274, ack 274, win 512, options [nop,nop,TS val 2375820430 ecr 2375820430], length 0
19 packets captured
56 packets received by filter
16 packets dropped by kernel

有谁知道,为什么内核会强制终止客户端? (通过发送 RST)?

编辑: 如果我使用 netcat 而不是客户端 nc 127.0.0.1 9877 &lt; somefile,服务器会正​​确地回显它,所以我怀疑客户端在某处有问题。但是内核还是强行让客户端发送RST,为什么呢?

【问题讨论】:

  • 只是一个想法......我熟悉write,但不熟悉Writen [但是,我假设它们相似]。您没有检查Writen 返回值。 Writen 可能会做一个“短”写,你必须循环(例如)while ((rlen = read(sockfd, buf, BSIZE)) &gt; 0) { for (off = 0; rlen &gt; 0; rlen -= slen, off += slen) { slen = Writen(sockfd, &amp;buf[off], rlen); if (slen &lt; 0) { perror("Writen"); break; } } }
  • 我猜客户端在某种程度上崩溃了,而 RST 是这次崩溃的结果。只是,您没有提供实际重现问题所需的信息,即没有完整的代码,没有来自客户端的输出,没有客户端崩溃的信息......。不要过分关注 RST,它可能只是客户端崩溃的结果。首先关注客户端崩溃的原因。

标签: c sockets tcp tcpdump


【解决方案1】:

问题是,在客户端发送完所有字节后,它不等待服务器回复并关闭所有套接字(进程退出(0),但是当进程退出时,内核关闭所有打开的文件描述符)。所以数据是在一端发送的,但在另一端回显之前,套接字已关闭。所以因为接收套接字的缓冲区不是空的,所以回复是RST。从客户端发送字节后,我制作了sleep(1) 的虚拟解决方案。之后它起作用了。

【讨论】:

    猜你喜欢
    • 2017-11-05
    • 1970-01-01
    • 2016-08-20
    • 1970-01-01
    • 2018-06-29
    • 1970-01-01
    • 2020-04-24
    • 1970-01-01
    • 2011-09-12
    相关资源
    最近更新 更多