【问题标题】:GetBytes returning different size than data byte actually isGetBytes 返回的大小与实际的数据字节不同
【发布时间】:2015-01-14 21:06:40
【问题描述】:

以下代码遍历 List<byte[]>(),但问题是由于某种原因,有时 lengthBuffer 与 byteData 的大小不同。

您可以在 DebugPrintInGame 方法中看到,我打印了 BitConverter.ToInt32() 长度,然后是实际的 byteDataList[i].Length,这两个数字应该完全相同,而且很多时候它们是相同的,但出于我不知道的原因现在很多时候他们也不是。

通过下面的代码,它将数据的长度发送到服务器,以便服务器知道传入数据的大小和预期的内容,然后发送实际数据。然而,有时它会发送比实际发送的数据更大的长度,但它是随机发生的,这让我相信这可能是一个线程问题——尽管如此 byteDataList 只在这个线程中使用,所以它甚至对我来说毫无意义锁定它。在我的测试中,我发送的数据不超过 2kb 到 17kb。

有没有人根据我项目中的这个小片段明白为什么会发生这种情况? 这段代码都是客户端注意 BitConverter.ToInt32(lengthBuffer, 0)byteDataList[i].Length 预计会给出相同的长度,但事实并非如此。这就是问题所在。

List<byte[]> byteDataList = new List<byte[]>();
//.........

lock (byteDataList) //pointless lock, just used for random testing :\
{
      if (byteDataList.Count > 0)
      {
             for (int i = 0; i < byteDataList.Count; i++)
             {
                 try
                 {
                      byte[] lengthBuffer = BitConverter.GetBytes(byteDataList[i].Length);
                      //quick printout to compare lengths for testing
                      this.QueueOnMainThread(() =>
                      {
                        Tools.DebugPrintInGame("Length buffer " + lengthBuffer.Length + 
                                               +  BitConverter.ToInt32(lengthBuffer,0)+ " "+
                                               + byteDataList[i].Length);  //this should be same as BitConverter lengthBuffer
                      });
                      //send length
                      stream.Write(lengthBuffer, 0, lengthBuffer.Length);
                      //send data
                      stream.Write(byteDataList[i], 0, byteDataList[i].Length);
                      stream.Flush();
                  }
                  catch (Exception ex)
                  {
                       this.QueueOnMainThread(() =>
                       {
                           Tools.DebugPrintInGame("Exception " + ex.ToString());
                       });
                  }
             }

      }
}

在发送到服务器之前从客户端打印出来:

当最后两个应该相同时,请注意不同的长度。 IE。

 `lengthBuffer`    = 17672 
 `byteDataList[i]` = 17672 

但有时它会变得奇怪和不稳定:

 `lengthBuffer`    = 17672
 `byteDataList[i]` = 2126

lengthBuffer 和 byteDataList[i] 应该相同。

【问题讨论】:

  • 是的,byte[] Array.Length 会给你一个字节数组的字节大小。
  • 是的,然后我将发送该数组元素。发送元素的长度,然后发送实际元素。看代码,注意[i]?
  • 出于调试目的,我认为您应该将长度(以字节为单位)添加到数据数组的开头,因为它是一个 int 的表示形式,它始终是相同的长度,因此您可以在以下位置读取和检查它们另一端以确保它不是延迟/网络问题。或者您是否也看到打印输出中的问题?
  • 帖子中的图片在发送到服务器之前来自客户端。
  • 整篇文章都是客户端代码。 lengthBuffer 和 byteDataList[i] 正在客户端上打印出来。看这段代码,都是客户端。问题是它在发送到服务器之前给出了错误的长度,原因我不知道,你可以在打印输出中看到。

标签: c# tcpclient netstream bitconverter


【解决方案1】:

这很可能是循环中 i 变量的闭包问题(我假设您使用的是 .NET 4.0 或更早版本?)罪魁祸首在这里:

this.QueueOnMainThread(() =>
                      {
                        Tools.DebugPrintInGame("Length buffer " + lengthBuffer.Length + 
                                               +  BitConverter.ToInt32(lengthBuffer,0)+ " "+
                                               + byteDataList[i].Length);  //this should be same as BitConverter lengthBuffer
                      });

看看你如何引用上面的变量i?上面的匿名方法在该变量上形成了一个闭包,这意味着在调用该方法之前不会评估i。这意味着i 的值可能不再是您用来创建lengthBuffer 的值,因此您得到了不匹配的结果。

要解决此问题,请将i 的副本存储到循环内部范围内的变量中,如下所示:

int k = i;
this.QueueOnMainThread(() =>
                          {
                            Tools.DebugPrintInGame("Length buffer " + lengthBuffer.Length + 
                                                   +  BitConverter.ToInt32(lengthBuffer,0)+ " "+
                                                   + byteDataList[k].Length);  //this should be same as BitConverter lengthBuffer
                          });

通过关闭一个内部范围变量,它将具有预期的值,并且不会随着循环的执行而改变。这里的混淆在于,为了闭包的目的,循环变量实际上被认为超出了循环体的范围。在更高版本的 C#(对于 .NET 4.5)中,他们改变了这种行为,并且循环变量现在被内部范围封闭。他们做出改变是因为在这种情况下很容易被这个问题绊倒。

【讨论】:

  • 是的,使用 .net 2.0,谢谢!现在谈谈为什么服务器接收的数据比预期的少。
  • @Euthyphro,您是否等待足够长的时间让所有 TCP 数据包到达?如果跨越数据包边界,单次接收可能无法获取所有数据,因此有必要缓冲和接收数据,直到获得完整的消息。但是,在您明确知道大小的情况下,大多数 TCP API 都会为您提供一种阻塞方式,直到收到正好 N 个字节。
  • 在服务器上我没有被 .net 2.0 卡住,所以我使用var initialBuffer = new byte[4]; var lengthBuffer = await receiveStream.ReadAsync(initialBuffer, 0,4); requestLength = BitConverter.ToInt32(initialBuffer, 0); 获取传入数据的长度,然后在var bytesRead = await receiveStream.ReadAsync( requestBuffer,0,requestLength); 之后根据预期长度接收实际数据。据我了解,这应该阻止并等待。如果大小不一样,我会抛出异常。
  • @Euthyphro,如果根本没有数据存在,ReadAsync 将“阻塞”,但如果字节数少于指定的计数,则不会阻塞。在读取整个缓冲区之前,您可能需要自己进行读取/缓冲。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-19
  • 2021-09-22
  • 1970-01-01
  • 1970-01-01
  • 2020-09-21
  • 1970-01-01
相关资源
最近更新 更多