在几分钟内发送少量数据后,windows 机器上的 TCP 发送方机器接收方 -window- 大小缩小为 0

TCP sender machine receiver-window-size shrinking to 0 on windows machine after sending tiny amounts of data for a few minutes

我正在编写一个在 windows 笔记本电脑上运行的应用程序,该应用程序循环发送 TCP 字节作为“自制保持活动”消息以保持连接活动(服务器计算机将在 15 后断开连接未收到 TCP 数据的秒数)。只要连接有效,“服务器机器”就会向笔记本电脑发送小块数据(大约 .5K bytes/second)(根据服务器文档,理想情况下这是一个“回声”数据包,但我无法找到这在 .NET 中是如何完成的)。我的问题是,当我在 Wireshark 中查看此数据时,我可以看到良好的网络 activity,然后,几分钟后,“win”(接收笔记本电脑上可用的 window 大小)从 65K 缩小到0 以每个数据包约 240 字节为增量。为什么会发生这种情况,我该如何预防?我似乎无法让 .Net 中的“keep-alive”标志起作用,所以这应该是我的解决方法。我没有看到任何遗漏的 ACK 消息,而且我的数据速率约为 2Kb/秒,所以我不明白为什么笔记本电脑 window 的大小在下降。我肯定认为我对 TCP 和/或 windows/.NET 使用 TCPsockets 存在误解,因为我没有使用 TCP 的经验(我一直使用 UDP)。

                    TcpClient client = new TcpClient(iPEndpoint);
                    //Socket s = client.Client; none of these flags actually work on the keep alive feature
                    //s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
                    //s.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveInterval, 10);
                    //s.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveInterval, 10);
    
                    // Translate the passed message into ASCII and store it as a Byte array.
                    // Byte[] data = System.Text.Encoding.ASCII.GetBytes(message);
    
                    IPAddress ipAdd = IPAddress.Parse("192.168.1.10"); 
                    IPEndPoint ipEndPoin = new IPEndPoint(ipAdd, 13000);
                    client.Connect(ipEndPoin);
    
                    NetworkStream stream = client.GetStream();
    
                    // Send the message to the connected TcpServer.
                    bool finished = false;
                    while (!finished)
                    {
                        try
                        {
                            stream.Write(data, 0, data.Length);
                            Thread.Sleep(5000);
                        }
                        catch (System.IO.IOException ioe)
                        {
                            if (ioe.InnerException is System.Net.Sockets.SocketException)
                            {
                                client.Dispose();
                                client = new TcpClient(iPEndpoint);
                                client.Connect(ipEndPoin); 
                                stream = client.GetStream(); 
                                Console.Write("reconnected");
                                // this imediately fails again after exiting this catch to go back to the while loop because send window is still 0
                            }
                        }
                    }

RFC 793, Transmission Control Protocol你应该不陌生,这是TCP的定义。它解释了 wondow 以及它如何用于流量控制:

Flow Control:

TCP provides a means for the receiver to govern the amount of data sent by the sender. This is achieved by returning a "window" with every ACK indicating a range of acceptable sequence numbers beyond the last segment successfully received. The window indicates an allowed number of octets that the sender may transmit before receiving further permission.

-和-

To govern the flow of data between TCPs, a flow control mechanism is employed. The receiving TCP reports a "window" to the sending TCP. This window specifies the number of octets, starting with the acknowledgment number, that the receiving TCP is currently prepared to receive.

-和-

Window: 16 bits

The number of data octets beginning with the one indicated in the acknowledgment field which the sender of this segment is willing to accept.

window 大小由数据接收方在其确认接收到数据的 ACK 段中决定。如果您的笔记本电脑接收 window 缩小到 0,它会将 window 设置为那个,因为它没有更多的 space 可以接收,并且需要时间来处理和释放space 在接收缓冲区中。当它有更多space时,它会发送一个更大window的ACK段。

Segment Receive  Test
Length  Window
------- -------  -------------------------------------------

   0       0     SEG.SEQ = RCV.NXT

   0      >0     RCV.NXT =< SEG.SEQ < RCV.NXT+RCV.WND

  >0       0     not acceptable

  >0      >0     RCV.NXT =< SEG.SEQ < RCV.NXT+RCV.WND
              or RCV.NXT =< SEG.SEQ+SEG.LEN-1 < RCV.NXT+RCV.WND

Note that when the receive window is zero no segments should be acceptable except ACK segments. Thus, it is be possible for a TCP to maintain a zero receive window while transmitting data and receiving ACKs. However, even when the receive window is zero, a TCP must process the RST and URG fields of all incoming segments.