问题描述
||
我目前正在编写一个简单的Web服务器,并有一些客户端可以使用它。我的客户希望能够将即将到来的解决方案的功能扩展到包括Web客户端,但是我们需要对通讯进行细微的控制,因此简单的Web服务器就是解决方案。
无论如何,有两种症状,我可以通过运行一系列单元测试将它们重现为100%。当我使用\“ POST \”命令将简单的字符串上载到服务器时,就会出现问题。实际上,这并不是我会真正做的事情,但是我无法理解所发生的事情而无法前进。我有一个单元测试,它使用BinaryFomatter简单地序列化了字符串“ Hello World!\”。我在结果字节数组数据前加上一个整数,该整数指示流数据的长度。当然,这是一个非常简单的协议,但它在所有其他情况下都可以正常工作(主要是大型对象图)。我有两种情况:
上传一个非常短的字符串(\“ Hello World!\”)
上载一个大字符串(几千个字符)。
当我不首先运行任何其他单元测试而运行该单元测试时,这可以按预期工作,但是每当我运行所有单元测试时,该测试总是以两种不同的方式失败:
短字符串似乎并没有触发接收套接字来接收它。更具体地说,当我调用Socket.BeginReceive()时,永远不会调用我的回调。
长字符串确实会按预期触发接收,但是流已损坏。长度前缀(4字节,序列化的Int32)包含一个非常大的值。当然不是正确的。
这是服务器代码中有趣的部分:
public void Receive(bool async = false,TimeSpan timeout = default(TimeSpan))
{
var asyncResult = _socket.BeginReceive(_lengthBuffer,_lengthBuffer.Length,SocketFlags.None,receiveLengthCallback,this);
if (!async)
Wait(timeout == default(TimeSpan) ? Timeout : timeout);
if (IsComplete)
return;
SocketError socketError;
_socket.EndReceive(asyncResult,out socketError);
SocketError = socketError;
}
private static void receiveLengthCallback(IAsyncResult asyncResult)
{
try
{
var data = (SocketDataReceiver)asyncResult.AsyncState;
var count = data._socket.EndReceive(asyncResult);
if (count == 0)
{
// connection was closed,abort ...
data.onReceiveAborted();
return;
}
data._index += count;
if (data._index < data._lengthBuffer.Length)
{
// length only partially received,get rest ...
data._socket.BeginReceive(data._buffer,data._index,data._lengthBuffer.Length - data._index,data);
return;
}
// done receiving the length prefix ...
data._length = BitConverter.ToInt32(data._lengthBuffer,0);
data.Data = new byte[data._length]; // ERROR (this will cause an OutOfMemoryException when data._length has become corrupted
if (data._length == 0)
{
// not much to do here,cancel ...
data.onReceiveAborted();
return;
}
data._index = 0;
if (data._buffer.Length > data._length)
data._buffer = new byte[data._length];
// start reading content ...
data._socket.BeginReceive(data._buffer,data._buffer.Length - data._index,receiveCallback,data);
}
catch (Exception ex)
{
// todo handle exception in Socket reception code
throw;
}
}
private static void receiveCallback(IAsyncResult asyncResult)
{
try
{
var data = (SocketDataReceiver)asyncResult.AsyncState;
var count = data._socket.EndReceive(asyncResult);
if (count == 0)
{
// connection was closed,abort ...
data.onReceiveAborted();
return;
}
foreach (var b in data._buffer)
{
data.Data[data._index++] = b;
if (--count == 0)
break;
}
if (data._index == data._length)
{
// all data has been received ...
data.onReceiveComplete();
return;
}
// more data is on the way ...
data._socket.BeginReceive(data._buffer,data._buffer.Length,data);
}
catch (Exception ex)
{
// todo handle exception in Socket reception code
throw;
}
}
我可能在这里得出了错误的结论,但是我没有看到流对象图有任何问题,而对序列化字符串执行相同操作是有问题的。我不明白为什么。我将不胜感激任何能为我指明正确方向的提示。
编辑
看来该问题是由先前的测试用例引起的,与发送字符串无关,这是我的第一个怀疑。有没有一种方法可以在两次连续的上载之间“徘徊”?但是,每次上载都会重新创建客户端套接字。
这是上传的客户端:
private void upload(string documentName,object data,int timeout = -1)
{
// todo Handle errors
WebClientEx client;
using (client = new WebClientEx())
{
client.Timeout = timeout < 0 ? UploadTimeout : timeout;
try
{
var response = client.UploadData(
new Uri(ServerUri + \"/\" + documentName),StreamedData.Wrap(data));
// todo Handle response
}
catch (Exception ex)
{
throw new Exception(\"Failed while uploading \" + data + \".\",ex);
}
}
GC.Collect(); // <-- this was just experimenting with getting rid of the client socket,for good measure. It has no effect on this problem though
}
干杯
/乔纳斯
解决方法
如果在第一次读取时捕获的字节太少,则仅在这次传递错误的缓冲区时才发出对
BeginRead
的另一个调用。我不是100%确定这是导致此特定问题的原因,但不正确:
if (data._index < data._lengthBuffer.Length)
{
// length only partially received,get rest ...
data._socket.BeginReceive(data._buffer,data._index,data._lengthBuffer.Length - data._index,SocketFlags.None,receiveLengthCallback,data);
return;
}