【发布时间】:2011-01-31 19:17:30
【问题描述】:
对于这篇冗长的帖子,我深表歉意。在传达问题的同时,我已将其尽可能小。
好吧,这快把我逼疯了。我有一个客户端和一个服务器程序,都是用 C# 编写的。服务器通过 Socket.Send() 向客户端发送数据。客户端通过 Socket.BeginReceive 和 Socket.Receive 接收数据。我的伪协议如下:服务器发送一个两字节(短)值,指示实际数据的长度,紧接着是实际数据。客户端异步读取前两个字节,将字节转换为短字节,然后立即从套接字同步读取那么多字节。
现在每隔几秒左右一个周期可以正常工作,但是当我提高速度时,事情变得很奇怪。似乎客户端在尝试从两字节长度读取时会随机读取实际数据。然后它会尝试将这两个任意字节转换为一个短字节,这会导致一个完全不正确的值,从而导致崩溃。以下代码来自我的程序,但经过修剪以仅显示重要的行。
服务器端发送数据的方法:
private static object myLock = new object();
private static bool sendData(Socket sock, String prefix, byte[] data)
{
lock(myLock){
try
{
// prefix is always a 4-bytes string
// encoder is an ASCIIEncoding object
byte[] prefixBytes = encoder.GetBytes(prefix);
short length = (short)(prefixBytes.Length + data.Length);
sock.Send(BitConverter.GetBytes(length));
sock.Send(prefixBytes);
sock.Send(data);
return true;
}
catch(Exception e){/*blah blah blah*/}
}
}
接收数据的客户端方法:
private static object myLock = new object();
private void receiveData(IAsyncResult result)
{
lock(myLock){
byte[] buffer = new byte[1024];
Socket sock = result.AsyncState as Socket;
try
{
sock.EndReceive(result);
short n = BitConverter.ToInt16(smallBuffer, 0);
// smallBuffer is a 2-byte array
// Receive n bytes
sock.Receive(buffer, n, SocketFlags.None);
// Determine the prefix. encoder is an ASCIIEncoding object
String prefix = encoder.GetString(buffer, 0, 4);
// Code to process the data goes here
sock.BeginReceive(smallBuffer, 0, 2, SocketFlags.None, receiveData, sock);
}
catch(Exception e){/*blah blah blah*/}
}
}
可靠地重现问题的服务器端代码:
byte[] b = new byte[1020]; // arbitrary length
for (int i = 0; i < b.Length; i++)
b[i] = 7; // arbitrary value of 7
while (true)
{
sendData(socket, "PRFX", b);
// socket is a Socket connected to a client running the same code as above
// "PRFX" is an arbitrary 4-character string that will be sent
}
查看上面的代码,可以确定服务器将永远发送数字1024,包括前缀在内的总数据的长度,作为一个短(0x400)后跟ASCII二进制中的“PRFX”后跟一个一堆 7 (0x07)。客户端将永远读取前两个字节 (0x400),将其解释为 1024,将该值存储为 n,然后从流中读取 1024 个字节。
这确实是它在前 40 次左右的迭代中所做的,但是,客户端会自然而然地读取前两个字节并将它们解释为 1799,而不是 1024!十六进制的 1799 是 0x0707,这是两个连续的 7!那是数据,而不是长度!那两个字节怎么了?无论我在字节数组中放入什么值都会发生这种情况,我只选择了 7,因为很容易看出与 1799 的相关性。
如果您此时仍在阅读,我为您的奉献精神鼓掌。
一些重要的观察:
- 减少 b 的长度会增加问题发生前的迭代次数,但不会阻止问题的发生
- 在循环的每次迭代之间添加显着延迟可能会阻止问题发生
- 当在同一主机上同时使用客户端和服务器并通过环回地址连接时,不会发生这种情况。
如前所述,这让我发疯!我总是能够解决我的编程问题,但这个问题完全难倒了我。因此,我在这里恳求有关此主题的任何建议或知识。
谢谢。
【问题讨论】: