Неожиданный пакет асинхронного сокета с BeginReceive \ EndReceive
Мне нужно получить асинхронное сообщение.
Во всех сообщениях первые 2 байта указывают длину следующего байтового массива. Моя проблема в том, что в нескольких случаях я получаю неожиданные пакеты.
Если я использую Thread.Sleep(200)
этой проблемы не бывает или случается редко.
Где я не прав?
protected void StartListening()
{
StateObject state = new StateObject() { ProcessHeader = true };
state.PrepareBuffer(HeaderLength);
lock (_secureConnection)
_secureConnection.BeginReceive(state.Buffer, 0, HeaderLength, 0, new AsyncCallback(ReadCallback), state);
}
private void ReadCallback(IAsyncResult ar)
{
if (_disposing)
return;
StateObject state = (StateObject)ar.AsyncState;
try
{
lock (_secureConnection)
_secureConnection.EndReceive(ar);
if (state.ProcessHeader)
{
state.ProcessHeader = !state.ProcessHeader;
var bodyLength = GetBodyLength(state.Buffer);
//Thread.Sleep(200);
state.CompleteMessage.AddRange(state.Buffer);
state.PrepareBuffer(bodyLength);
lock (_secureConnection)
_secureConnection.BeginReceive(state.Buffer, 0, bodyLength, 0, new AsyncCallback(ReadCallback), state);
}
else
{
state.CompleteMessage.AddRange(state.Buffer);
ProcessMessage(state.CompleteMessage); //process this message
//Thread.Sleep(200);
state.ProcessHeader = !state.ProcessHeader;
state.CompleteMessage.Clear();
state.PrepareBuffer(HeaderLength);
lock (_secureConnection)
_secureConnection.BeginReceive(state.Buffer, 0, HeaderLength, 0, new AsyncCallback(ReadCallback), state);
}
}
catch (Exception e)
{
Close(true);
}
}
class StateObject
{
public StateObject()
{
ProcessHeader = true;
}
public byte[] Buffer { get; private set; }
public bool ProcessHeader { get; set; }
public List<byte> CompleteMessage = new List<byte>();
public void PrepareBuffer(int size)
{
Buffer = new byte[size];
}
}
2 ответа
Вы предполагаете, что TCP является протоколом на основе сообщений. Впрочем, это поток байтов. Ваши чтения могут прочитать любое количество больше нуля. Это так же, как с FileStream. Файлы не имеют сообщений, либо.
Ваш код должен иметь дело с этим фактом. Ищите "Обрамление сообщения TCP".
НАЙДЕНО РЕШЕНИЕ Спустя долгое время я написал хорошее решение и надеюсь помочь кому-нибудь. Большое спасибо за ваши предложения.
int HeaderLength = 2;
int bodyLength;
int bytesReceived;
int totalBytesReceived;
protected void StartListening()
{
StateObject state = new StateObject() { ProcessHeader = true };
state.PrepareBuffer(HeaderLength);
lock (_secureConnection)
_secureConnection.BeginReceive(state.Buffer, 0, HeaderLength, 0, new AsyncCallback(ReadCallback), state);
}
/// <summary>
/// Reads the callback.
///
/// A message is in this form:
///
/// 2 bytes indicate the leght of body message (n)
/// n bytes for body message
///
/// </summary>
/// <param name="ar">IAsyncResult.</param>
private void ReadCallback(IAsyncResult ar)
{
if (_disposing)
return;
StateObject state = (StateObject)ar.AsyncState;
try
{
lock (_secureConnection)
bytesReceived = _secureConnection.EndReceive(ar);
if (state.ProcessHeader) //In this phase I receive 2 bytes that indicate the total length of the next message
{
state.ProcessHeader = !state.ProcessHeader;
bodyLength = GetBodyLength(state.Buffer); //I interpret 2 bytes to know body message length
state.CompleteMessage.AddRange(state.Buffer);
state.PrepareBuffer(bodyLength);
totalBytesReceived = bytesReceived = 0;
lock (_secureConnection)
_secureConnection.BeginReceive(state.Buffer, 0, bodyLength, 0, new AsyncCallback(ReadCallback), state);
}
else //In this phase I receive the message, with one or more recursive CallBack
{
state.CompleteMessage.AddRange(state.Buffer.ToList().GetRange(0, bytesReceived));
totalBytesReceived += bytesReceived;
int totalBytesMissing = bodyLength - totalBytesReceived;
if (totalBytesReceived < bodyLength)
{
state.PrepareBuffer(totalBytesMissing);
lock (_secureConnection)
_secureConnection.BeginReceive(state.Buffer, 0, totalBytesMissing, 0, new AsyncCallback(ReadCallback), state);
return;
}
//totalMessageLenght = body length plus first 2 bytes indicate body length
int totalMessageLenght = bodyLength + 2;
var completeMessage = state.CompleteMessage.GetRange(0, totalMessageLenght).ToList();
ProcessMessage(completeMessage);
state.ProcessHeader = !state.ProcessHeader; //I prepare Callback to read 2 bytes indicate the total length of the next message
state.CompleteMessage.Clear();
state.PrepareBuffer(HeaderLength);
lock (_secureConnection)
_secureConnection.BeginReceive(state.Buffer, 0, HeaderLength, 0, new AsyncCallback(ReadCallback), state);
}
}
catch (Exception e)
{
Close(true);
}
}