我目前正在编写一个简单的Web服务器和一些客户端来使用它。我的客户希望能够扩展即将推出的解决方案的功能以包括Web客户端,但我们需要对通信进行精细控制,因此可以使用简单的Web服务器。
无论如何,有两个症状,我可以通过运行一堆单元测试将它们重现为100%。当我使用“POST”命令将简单字符串上传到服务器时,问题就出现了。这不是我在现实中会做的事情,但我无法继续前进而不了解正在发生的事情。我有一个单元测试,只需使用BinaryFomatter序列化字符串“Hello World!”。我在结果字节数组数据前加一个表示流数据长度的整数。一个非常简单的协议肯定,但它在所有其他情况下工作得很好(主要是大型对象图)。我有两种情况:
当我在没有先运行任何其他单元测试的情况下运行该单元测试时,这可以按预期工作,但每当我运行所有单元测试时,这一次总是以两种不同的方式失败:
这是服务器代码的有趣部分:
public void Receive(bool async = false, TimeSpan timeout = default(TimeSpan))
{
var asyncResult = _socket.BeginReceive(_lengthBuffer, 0, _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, SocketFlags.None, receiveLengthCallback, 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._index, data._buffer.Length - data._index, SocketFlags.None, 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, 0, data._buffer.Length, SocketFlags.None, receiveCallback, 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
}
干杯
/纳斯
答案 0 :(得分:1)
如果你在第一次读取时捕获的字节太少,那么只有在你传入错误的缓冲区时才会发出另一个对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;
}