TcpClient/Server NetworkStream正在跳过数据包

本文关键字:数据包 Server NetworkStream TcpClient | 更新日期: 2023-09-27 18:25:47

在我的程序中的某些时刻,从不同的线程到服务器的写入几乎是同时进行的。我注意到,当两者几乎立即发送时,其中一个永远不会到达服务器,尽管使用了TCP,应该确保收到数据包。

为了测试这一点,我只需一个接一个地发送1000个数据包,没有延迟:

NetworkStream stream = server.GetStream();
int i = 0;
while (i < 1000)
{
    byte[] data = Encoding.Unicode.GetBytes(i.ToString() + "$");
    stream.Write(data, 0, data.Length);
    i++;
}

并接收:

bool LostConnection = false;
NetworkStream stream = client.GetStream()
while (ClientSocket.Connected && !LostConnection && !AppIsClosing)
{
    try
    {
        byte[] ReceivedBytes = new byte[4096];
        if (stream.Read(ReceivedBytes, 0, ReceivedBytes.Length) == 0)
        { LostConnection = true; break; }
        string DataFromClient = Encoding.Unicode.GetString(ReceivedBytes);
        if (DataFromClient.Contains("$"))
        {
            DataFromClient = DataFromClient.Substring(0, DataFromClient.IndexOf("$"));
            Console.WriteLine(" >> " + DataFromClient);
        }
    }
    catch { }
}

输出:

 >> 0
 >> 1
 >> 20
 >> 76
 >> 108
 >> 136
 >> 163
 >> 197
 >> 220
 >> 241
 >> 276
 >> 302
 >> 330
 >> 372
 >> 373
 >> 469
 >> 507
 >> 530
 >> 560
 >> 590
 >> 628
 >> 651
 >> 683
 >> 722
 >> 747
 >> 772
 >> 803
 >> 826
 >> 861
 >> 889
 >> 913
 >> 950
 >> 980

是什么导致了这种现象?

附言:我也试过:

var result = stream.WriteAsync(data, 0, data.Length);
result.Wait();

运气不好。。。

更新:

好吧,我很笨。流包含所有其他堆积起来的文本,我只是用Substring(0, IndexOf("$"))将其删除所以现在我只是用新行替换$,但后来我遇到了另一个问题。在800-900左右的某个地方,它再次从500开始计数,然后达到999。这是怎么回事?

TcpClient/Server NetworkStream正在跳过数据包

重大建议,请阅读并理解此链接。您需要连同您的数据一起发送一些关于您正在发送的内容的元数据。比如你发送了多少字节。如果你发送"555$",那么你必须在这个前缀前说:我发送4个字节。您需要有一个能够理解这一点的监听器,并等待4个字节。

我在这里引用上面的链接

Okay, so let's think about the possible situations that 
might occur with the data that the server receives in one receive operation:
  1. 在第一次接收操作中,接收的字节数少于前缀
  2. 在先前的一个或多个接收操作上具有前缀的接收部分之后,然后接收前缀的另一部分,但不接收所有这些
  3. 在之前的一次或多次接收操作中接收到部分前缀后,再接收前缀的其余部分,但仅限于此
  4. 在之前的一个或多个接收操作中接收到前缀的一部分后,我们将接收它的其余部分,以及消息的一部分
  5. 在之前的一个或多个接收操作中接收到部分前缀后,我们将接收其余前缀以及所有消息
  6. 接收前缀中的确切字节数,但仅限于此
  7. 在准确地接收到前一个或多个接收操作的前缀中的字节数后,我们将接收消息
  8. 在准确地接收到前一个或多个接收操作的前缀中的字节数之后,我们将接收所有消息
  9. 接收前缀的字节数加上消息的一部分,但不是全部消息
  10. 在之前的一个或多个接收操作中接收到前缀和消息的一部分后,我们将接收信息,但不是全部
  11. 在之前的一个或多个接收操作中接收到前缀和部分消息后,我们将接收消息
  12. 在第一个接收操作上接收前缀的字节数加上所有消息

最后一件事实际上是将要发生的最常见的事情。但所有上面的事情可以发生,而且确实会发生。如果两个客户端和服务器的缓冲区大小大于消息,则当同时运行客户端和服务器在同一台机器上,甚至在局域网上。但是TCP更重要在数据通过的互联网上不可预测多台机器。因此,您的代码需要允许所有这些可能性。

我认为问题出在这条线上

        DataFromClient = DataFromClient.Substring(0, DataFromClient.IndexOf("$"));

您假设每次读取将获得1个字符串,但一次读取可能有许多可用字符串。您应该尝试使用split("$"),并将所有结果写入