【发布时间】:2018-12-05 21:58:01
【问题描述】:
对于某些程序,我想将数据从 python 程序发送到 c# 程序。我将所有数据放入 python 中的列表中,并在转换为字节后发送(将它们打包为双精度,所以我发送的每个数字有 8 个字节)。了解了套接字和 TCP 流的工作原理后,列表中的第一个数字是列表其余部分占用的字节数。因此,流的前 8 个字节告诉我需要读取多少字节才能获取所有其他数据。
但是,当我调用 BeginRead 并调用回调时,它读取的内容比我要求它读取的少 8 个字节。例如,前 8 个字节告诉我有 116432 个字节要读取,但是当我调用 EndRead 时它返回 116424。
好吧,那又怎样?缺少八个字节,这相当于丢失了一个双倍。这本身就是一个问题,但我什至弄清楚了这个替身去了哪里。
在 python 中,在我的数据中的特定点(虽然它仍然是两倍),我看到我正在发送:"...,1961.0, 0.0128, 2033.0, 0.0442, 2034.0,..." 当我在 c# 中检查同一点时(将我的字节转换回双精度后),我看到:"..,1961.0, 2033.0002, 0.0442,2034.0,..."。
对我来说,很明显这 8 个字节不知何故被混为一个,以某种方式将两个数字(可能是按位?)融合在一起。我还注意到,字节数据中出现这种情况的索引大致在 64k-65k 标记处。所以我怀疑 64kbytes 是 TCP 数据包的最大数据包大小,流有某种打嗝并覆盖我的缓冲区的一部分而不清除它,导致一些文字混淆?有谁知道我该如何解决这个问题,或者我犯了什么错误导致这种情况发生?
我将两个相关的函数贴在这里。
private void Listen(int port)
{
try
{
// Perform a blocking call to accept requests.
// You could also user server.AcceptSocket() here.
var client = _server.AcceptTcpClient();
// Get a stream object for reading and writing
var stream = client.GetStream();
var pLength = new byte[8];
// Loop to receive all the data sent by the client.
while(_running)
{
if(!stream.DataAvailable && stream.Read(pLength, 0, 8) <= 0)
continue;
var nOfBytes = (int) BitConverter.ToDouble(pLength, 0);
pLength = new byte[8];
if (nOfBytes <= 0)
{
continue;
}
var localBytes = new byte[nOfBytes];
var scriptData = new ScriptData(stream, localBytes);
stream.BeginRead(localBytes, 0, nOfBytes, new AsyncCallback(GotAllBytes), scriptData);
}
// Shutdown and end connection
client.Close();
}
catch (SocketException e)
{
_errorBool = true;
_errorString = "Port: " + port + "\n" + e.Message + e.StackTrace;
}
finally
{
// Stop listening for new clients.
_server.Stop();
}
}
private void GotAllBytes(IAsyncResult result)
{
var scriptData = (ScriptData)result.AsyncState;
if (OnlinePaused)
{
scriptData.Stream.EndRead(result);
return;
}
var bytesRead = scriptData.Stream.EndRead(result);
_rawDataQueue.Enqueue(scriptData.Buffer.ToList());
}
感谢阅读,希望对你有所帮助。
【问题讨论】:
-
Read保证给您的所有内容(如果成功)是 1 个字节。如果您想要/需要读取一定数量的字节,则必须循环(并调整您要求数据去往的缓冲区中的位置以及要读取的数量)。或者换句话说,GotAllBytes被乐观但错误地命名。即使您最初读取 8 个字节,也不能保证一次获得所有字节。 -
这听起来像是
BinaryReader的工作。 -
@Damien_The_Unbeliever 是的,我知道是这样。但是,我很难相信我总是错过数据的最后 8 个字节,而这两个特定数字似乎混合在一起。据我了解,BeginRead 只会在确实读取 nOfBytes 个字节后才调用“GotAllBytes”。如果我错了,请纠正我。
-
@JeroenMostert 确实如此!我已经在我的代码的另一部分中使用了 BinaryReader。事实上,在 _rawDataQueue 中将数据排入队列后,我在其他地方出列并使用 BinaryReader 将其转换回双精度数。这就是我注意到这两点似乎混合在一起的原因。
-
看看
BinaryReader.FillBuffer是如何实现的;你会看到它使用了 Damien 提到的那种循环来确保它读取必要的字节。Stream.Read不保证给你特定数量的字节;只有建立在流之上的各种阅读器类才能这样做。您发现“难以置信”的情况可能正是数据包在本地接口上被截断时发生的情况;你只是(不)幸运的数据并没有更加碎片化。像这样的代码甚至可以运行得很好(偶然),然后在其他情况下会失败。
标签: c# asynchronous