【问题标题】:TcpClient / Server NetworkStream skipping packetsTcpClient / Server NetworkStream 跳过数据包
【发布时间】:2014-10-07 17:53:02
【问题描述】:

在我的程序中的某些时刻,我有时会几乎同时从不同的线程向服务器进行写入。我注意到,当两者都几乎立即发送时,尽管使用了 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。这可能是什么原因?

【问题讨论】:

  • 您不使用 stream.Read 返回的内容。 Read 可能一次返回多个数据包或一半数据包。
  • 也不要把catch留空,也许你会遇到异常!
  • 那我用什么,好心的先生?
  • 另外,出于调试目的,将其放在 if(contain($)): Console.WriteLine(" Raw Data >> " + DataFromClient);
  • @PkKingX11,而不是$使用\r\n作为分隔符,那么你可以使用StreamReader类的ReadLine方法。这样生活会更轻松。

标签: c# .net winforms


【解决方案1】:

严重的建议,go read and understand this link。您需要与您的数据一起发送一些关于您正在发送的内容的元数据。比如你发送了多少字节。如果您发送“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更多 在数据通过的互联网上不可预测 多台机器。所以你的代码需要允许所有这些 可能性。

【讨论】:

  • then you have to prefix this,不,这只是可以使用的技术之一。分隔符字符/字符串也可以工作。例如,twitter 的流 API 使用 NewLine char,
【解决方案2】:

我认为问题出在这一行

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

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

【讨论】:

  • 这仍然不是一个好的解决方案,他可能会在一个数据包中得到$99,然后在下一个数据包中得到8$999
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-11
  • 2012-03-26
  • 2020-10-01
  • 2012-12-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多