Мне нужна помощь в выяснении того, как устранить проблему, свидетелем которой я являюсь при передаче большого объема данных через TCP с использованием сокетов .NET.
В двух словах, при запуске клиентского приложения оно подключается кконкретный порт на сервере.После подключения сервер начинает отправку данных в реальном времени клиенту, который отображает информацию в виде тикера.Сервер поддерживает несколько клиентских рабочих станций, поэтому данные будут отправляться через несколько портов (несколько сокетов).
Все реализовано и отлично работает с медленной подачей и малым объемом.Я проводил стресс-тестирование системы, чтобы обеспечить ее устойчивость и масштабируемость.Когда я увеличиваю частоту, сервер работает отлично.Тем не менее, я вижу то, что, кажется, потерянные пакеты на клиентах.Это происходит в случайные моменты времени.
В настоящее время каждому транслируемому сообщению предшествует 4-байтовое значение, определяющее длину этого сообщения.Когда мы получаем данные в клиенте, мы добавляем данные в буфер (поток), пока не получим это количество байтов.Любые дополнительные байты считаются началом следующего сообщения.Опять же, это прекрасно работает, пока я не увеличу частоту.
В моем тесте я отправляю пакет размером около 225 байтов, за которым следует один из примерно 310 КБ, а другой - около 40 КБ.Отправка сообщения каждую 1 секунду работает без сбоев с 12 запущенными клиентами.Увеличив частоту до 1/2 секунды, я в итоге увидел, что один из дисплеев клиента завис.Переходя к 1/4 секунды, я могу воспроизвести проблему всего с 4 клиентами в течение нескольких секунд.
Глядя на мой код (который я могу предоставить, если необходимо), я вижу, что все клиентыполучают данные, но каким-то образом информация «вышла из синхронизации», и ожидаемое значение длины огромно (в диапазоне 100 миллионов).В результате мы просто продолжаем читать данные и никогда не воспринимаем конец сообщения.
Мне нужен либо лучший подход, либо способ убедиться, что я получаю ожидаемые данные, а не теряю пакеты.Можете ли вы помочь?
ОБНОВЛЕНИЕ
Я провел тонну дополнительного тестирования, варьируя размер сообщений и частоту доставки.Определенно есть корреляция.Чем меньше я создаю размеры сообщений, тем выше частота, которую я могу достичь.Но неизбежно, я всегда могу это сломать.
Итак, более точно описать то, что я ищу, это:
Чтобы понять, что происходит.Это поможет мне определить возможное решение или, как минимум, установить пороги для надежного поведения.
Внедрить отказоустойчивый механизм, чтобы при возникновении проблемы я мог справиться с ним и, возможно,оправиться от этого.Возможно добавление контрольной суммы в поток данных или что-то в этом роде.
Вот код, который я запускаю в клиентских (принимающих) приложениях:
public void StartListening(SocketAsyncEventArgs e)
{
e.Completed += SocketReceive;
socket.ReceiveAsync(e);
}
private void SocketReceive(Object sender, SocketAsyncEventArgs e)
{
lock (_receiveLock)
{
ProcessData(e.Buffer, e.BytesTransferred);
socket.ReceiveAsync(e);
}
}
private void ProcessData(Byte[] bytes, Int32 count)
{
if (_currentBuffer == null)
_currentBuffer = new ReceiveBuffer();
var numberOfBytesRead = _currentBuffer.Write(bytes, count);
if (_currentBuffer.IsComplete)
{
// Notify the client that a message has been received (ignore zero-length, "keep alive", messages)
if (_currentBuffer.DataLength > 0)
NotifyMessageReceived(_currentBuffer);
_currentBuffer = null;
// If there are bytes remaining from the original message, recursively process
var numberOfBytesRemaining = count - numberOfBytesRead;
if (numberOfBytesRemaining > 0)
{
var remainingBytes = new Byte[numberOfBytesRemaining];
var offset = bytes.Length - numberOfBytesRemaining;
Array.Copy(bytes, offset, remainingBytes, 0, numberOfBytesRemaining);
ProcessData(remainingBytes, numberOfBytesRemaining);
}
}
}
internal sealed class ReceiveBuffer
{
public const Int32 LengthBufferSize = sizeof(Int32);
private MemoryStream _dataBuffer = new MemoryStream();
private MemoryStream _lengthBuffer = new MemoryStream();
public Int32 DataLength { get; private set; }
public Boolean IsComplete
{
get { return (RemainingDataBytesToWrite == 0); }
}
private Int32 RemainingDataBytesToWrite
{
get
{
if (DataLength > 0)
return (DataLength - (Int32)_dataBuffer.Length);
return 0;
}
}
private Int32 RemainingLengthBytesToWrite
{
get { return (LengthBufferSize - (Int32)_lengthBuffer.Length); }
}
public Int32 Write(Byte[] bytes, Int32 count)
{
var numberOfLengthBytesToWrite = Math.Min(RemainingLengthBytesToWrite, count);
if (numberOfLengthBytesToWrite > 0)
WriteToLengthBuffer(bytes, numberOfLengthBytesToWrite);
var remainingCount = count - numberOfLengthBytesToWrite;
// If this value is > 0, then we have still have more bytes after setting the length so write them to the data buffer
var numberOfDataBytesToWrite = Math.Min(RemainingDataBytesToWrite, remainingCount);
if (numberOfDataBytesToWrite > 0)
_dataBuffer.Write(bytes, numberOfLengthBytesToWrite, numberOfDataBytesToWrite);
return numberOfLengthBytesToWrite + numberOfDataBytesToWrite;
}
private void WriteToLengthBuffer(Byte[] bytes, Int32 count)
{
_lengthBuffer.Write(bytes, 0, count);
if (RemainingLengthBytesToWrite == 0)
{
var length = BitConverter.ToInt32(_lengthBuffer.ToArray(), 0);
DataLength = length;
}
}
}