Буфер, используемый для TcpClient, перезаписывается, прежде чем сообщение может быть прочитано. - PullRequest
1 голос
/ 17 февраля 2012

Для отправки данных по TCP-соединению для моего приложения я использую простой фрагмент кода:

public void Send(byte[] message)
{
    if (socket != null)
    {
        if (stream != null)
        {
            stream.Write(message, 0, message.Length);
            if (receiveThread == null)
            {
                StartReceiver();
            }
        }
    }
}

Сокет является экземпляром класса TcpClient, а поток - связаннымэкземпляр потока.StartReceiver() запускает поток, который, как предполагает метод, получает данные, отправленные приложению.

Для получения данных я использую:

private void ReceiveLoop()
{
    DataReceivedStruct drs;
    try
    {
        for (; ; )
        {
            if (stream != null)
            {
                drs = new DataReceivedStruct();
                drs.stream = stream;
                drs.waitHandle = are;
                stream.BeginRead(readBuffer, 0, readBuffer.Length, DataReceived, drs);
                Console.WriteLine("Waiting to be allowed to continue");
                are.WaitOne();
                Console.WriteLine("Allowed, continuing loop");
            }
            else
            {
                Thread.Sleep(5);
            }
        }
    }
    catch (SocketException e)
    {
        DispatchRaiseException(e);
    }
    catch (Exception e)
    {
        DispatchRaiseException(e);
    }
}

Опять же, используемый поток - это вышепотоковый экземпляр объекта класса TcpClient.Объект readBuffer является byte[1024].Обратный вызов BeginRead выглядит следующим образом:

private void DataReceived(IAsyncResult result)
{
    DataReceivedStruct drs = (DataReceivedStruct)result.AsyncState;
    NetworkStream used = drs.stream;
    AutoResetEvent handle = drs.waitHandle;
    used.EndRead(result);
    DispatchRaiseReceived(readBuffer);
    Console.WriteLine("Signalling allowance of continue for loop");
    handle.Set();
}

Завершает действие чтения в потоке и передает набор данных в readBuffer.

Это работает в принципе.Я могу отправлять и получать данные из и в приложение.Существует только одна проблема с получающей стороной заявки.Когда в приложение отправляется сообщение, вызывается функция BeginRead, после чего обратный вызов запускает и завершает операцию чтения с помощью EndRead и передает данные для дальнейшей обработки.Это работает для одного сообщения за раз.Но становится интереснее, когда другое сообщение отправляется сразу после того, как первое сообщение вызвало BeginRead.Затем происходит то, что EndRead для первого сообщения еще не произошло, поэтому данные первого сообщения записываются вторым сообщением, что приводит к неверным данным.

Если я перестану использовать BeginRead / EndRead и просто использовать блокирующую операцию Read для получения данных?Или можно заблокировать поток с помощью BeginRead / EndRead, чтобы второе сообщение не было получено, пока не обработано первое сообщение?

1 Ответ

1 голос
/ 17 февраля 2012

ИМО, проблема здесь в том, чтобы думать в терминах цикла, поэтому требуется флаг продолжения. Это не масштабируется, так как для каждого соединения требуется поток.

Что вы должны сделать, это:

  • получить данные для отправки
  • начать прием асинхронно (я использую ReceiveAsync, но BeginRead тоже должен работать)
  • и выход! (без петли)

В обратном вызове:

  • обработать фрагмент (или поместить его в буфер)
  • начать получать при необходимости
  • и выход!

Если вы готовы чередовать несколько разных буферов (я использую небольшой микро-пул), вы можете поменять местами «процесс», чтобы продолжить чтение параллельно с обработкой. Но тогда вам абсолютно необходимы разные буферы для каждого чтения, чтобы предотвратить перезапись данных. Это обычно требуется только для исключительно сценариев с высоким уровнем чтения.

Если это поможет, я работаю над библиотекой для написания простых сценариев TCP / клиент-сервер, не беспокоясь о всех неровных деталях реализации, которые я планирую выпустить как open-source, как только станет стабильным; с широким пулом / повторным использованием объектов, полностью асинхронным использованием (с API 3.5, который привязывается к портам завершения) и т. д.

...