C # Socket BeginReceive / EndReceive захват нескольких сообщений - PullRequest
3 голосов
/ 17 августа 2010

Проблема:

Когда я делаю что-то вроде этого:

for (int i = 0; i < 100; i++)
{
    SendMessage( sometSocket, i.ToString());
    Thread.Sleep(250); // works with this, doesn't work without
}

С или без сна сервер регистрирует отправку отдельных сообщений. Однако без сна клиент в конечном итоге получает несколько сообщений в одном OnDataReceived, поэтому клиент будет получать сообщения типа:

0, 1, 2, 34, 5, 678, 9 ....

Код отправки сервера:

private void SendMessage(Socket socket, string message)
{
    logger.Info("SendMessage: Preparing to send message:" + message);            

    byte[] byteData = Encoding.ASCII.GetBytes(message);

    if (socket == null) return;
    if (!socket.Connected) return;

    logger.Info("SendMessage: Sending message to non " +
                "null and connected socket with ip:" + socket.RemoteEndPoint);

    // Record this message so unit testing can very this works.

    socket.Send(byteData);
}

Клиент, получающий код:

private void OnDataReceived(IAsyncResult asyn)
{
    logger.Info("OnDataReceived: Data received.");

    try
    {
        SocketPacket theSockId = (SocketPacket)asyn.AsyncState;
        int iRx = theSockId.Socket.EndReceive(asyn);
        char[] chars = new char[iRx + 1];
        System.Text.Decoder d = System.Text.Encoding.UTF8.GetDecoder();
        int charLen = d.GetChars(theSockId.DataBuffer, 0, iRx, chars, 0);
        System.String szData = new System.String(chars);

        logger.Info("OnDataReceived: Received message:" + szData);

        InvokeMessageReceived(new SocketMessageEventArgs(szData));

        WaitForData();  // .....  

Пакет розеток:

public class SocketPacket
{
    private Socket _socket;
    private readonly int _clientNumber;
    private byte[] _dataBuffer = new byte[1024]; ....

Я догадываюсь, что это связано с размером буфера или просто между OnDataReceived и EndReceive мы получаем несколько сообщений.

Обновление: получается, когда я ставлю Thread.Sleep в начале OnDataReceived, он получает каждое сообщение. Является ли единственным решением этой проблемы завершение моего сообщения префиксом длины и строкой, обозначающей конец?

Ответы [ 3 ]

11 голосов
/ 17 августа 2010

Это ожидаемое поведение. Сокет TCP представляет собой линейный поток байтов, не последовательность хорошо разделенных «пакетов».Вы не должны предполагать, что данные, которые вы получаете, разбиваются на части так же, как и при отправке.

Обратите внимание, что это имеет два последствия:

  1. Двасообщения могут быть объединены в один обратный вызов.(Вы заметили это.)
  2. Одно сообщение может быть разбито (в любой момент) на два отдельных обратных вызова.

Ваш код должен бытьнаписан для обработки обоих этих случаев, в противном случае он содержит ошибку.

0 голосов
/ 18 августа 2010

Нет необходимости отказываться от Tcp, потому что он ориентирован на поток.

Вы можете устранить возникшие проблемы, внедрив кадрирование сообщений.

См.

http://blogs.msdn.com/malarch/archive/2006/06/26/647993.aspx

также:

http://nitoprograms.blogspot.com/2009/04/message-framing.html

0 голосов
/ 17 августа 2010

TCP-сокеты не всегда отправляют данные сразу - чтобы минимизировать сетевой трафик, реализации TCP / IP часто буферизуют данные на некоторое время и отправляют их, когда он видит, что затишье (или когда буфер заполнен).

Если вы хотите, чтобы сообщения обрабатывались одно за другим, вам нужно либо установить socket.NoDelay = true (что может не сильно помочь, так как полученные данные могут по-прежнему объединяться в получателе).буфер), реализовать некоторый протокол для разделения сообщений в потоке (например, префикс каждого сообщения по длине или, возможно, использование CR / LF для их разделения), или использовать протокол, ориентированный на сообщения, такой как SCTP (который может не поддерживаться без дополнительного программного обеспечения) или UDP (если вы можете справиться с потерей сообщений).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...