В настоящее время я пишу простой веб-сервер и некоторые клиенты для его использования.Мой клиент хочет иметь возможность расширить функциональность грядущего решения, включив в него веб-клиентов, но нам нужно иметь минимальный контроль над коммуникацией, чтобы решение было простым веб-сервером.
В любом случае, есть два признака иЯ могу воспроизвести их оба на 100%, запустив кучу модульных тестов.Проблема возникает, когда я загружаю простую строку на сервер, используя команду «POST».На самом деле это не то, чем я буду заниматься, но я не могу двигаться вперед, не понимая, что происходит.У меня есть модульный тест, который просто сериализует строку «Hello World!», Используя BinaryFomatter.Я ставлю перед полученными данными массива байтов целое число, указывающее длину потоковых данных.Конечно, очень простой протокол, но он отлично работает во всех других ситуациях (в основном это большие графы объектов).У меня есть два сценария:
- Загрузка очень короткой строки («Hello World!»)
- Загрузка большой строки (несколько тысяч символов).
Когда я запускаю этот модульный тест без предварительного запуска каких-либо других модульных тестов, он работает как положено, но всякий раз, когда я запускаю все свои модульные тесты, этот тест всегда дает сбой двумя различными способами:
- Короткая строкаКажется, он вызывает приемный сокет для его получения.Более конкретно, когда я вызываю Socket.BeginReceive (), мой обратный вызов никогда не вызывается.
- Длинная строка вызывает ожидаемый прием, но поток искажается.Префикс длины (4 байта, сериализованный Int32) содержит очень большое значение.Конечно, не правильный.
Это интересная часть серверного кода:
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
}
Cheers
/ Jonas