Windows socket.Send данные не принимаются, пока socket.Close - PullRequest
4 голосов
/ 16 февраля 2010

Я разрабатываю серверное приложение, которое асинхронно принимает TCP-соединения (BeginAccept / EndAccept) и данные (BeginReceive / EndReceive). Протокол требует, чтобы ACK отправлялся всякий раз, когда обнаруживается символ EOM, прежде чем он отправит следующее сообщение. Прием и получение работают, но отправляющее приложение не получает ACK (отправлено синхронно).

    private void _receiveTransfer(IAsyncResult result)
    {
        SocketState state = result.AsyncState as SocketState;
        int bytesReceived = state.Socket.EndReceive(result);

        if (bytesReceived == 0)
        {
            state.Socket.Close();
            return;
        }

        state.Offset += bytesReceived;
        state.Stream.Write(state.Buffer, 0, bytesReceived);

        if (state.Buffer[bytesReceived - 1] == 13)
        {
            // process message
            Messages.IMessage message = null;
            try
            {
                var value = state.Stream.ToArray();

                // do some work
                var completed = true;

                if (completed)
                {
                    // send positive ACK
                    var ackMessage = string.Format(ack, message.TimeStamp.ToString("yyyyMMddhhmm"), message.MessageType, message.Id, "AA", message.Id);
                    var buffer = ASCIIEncoding.ASCII.GetBytes(ackMessage);
                    int bytesSent = state.Socket.Send(buffer, 0, buffer.Length, SocketFlags.None);
                }
                else
                {
                    // send rejected ACK
                    var ackMessage = string.Format(ack, message.TimeStamp.ToString("yyyyMMddhhmm"), message.MessageType, message.Id, "AR", message.Id);
                    state.Socket.Send(ASCIIEncoding.ASCII.GetBytes(ackMessage));
                }
            }
            catch (Exception e)
            {
                // log exception


                // send error ACK
                if (message != null)
                {
                    var ackMessage = string.Format(ack, DateTime.Now.ToString("yyyyMMddhhmm"), message.MessageType, message.Id, "AE", message.Id);
                    state.Socket.Send(ASCIIEncoding.ASCII.GetBytes(ackMessage));
                }
            }
        }

        state.Socket.BeginReceive(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, new AsyncCallback(_receiveTransfer), state);
    }

State.Socket.Send возвращает правильное количество байтов, но данные не будут получены, пока сокет не будет удален.

Предложения приветствуются.

Ответы [ 4 ]

2 голосов
/ 16 февраля 2010
  • вы не должны делать ничего синхронного из процедур асинхронного завершения. Под нагрузкой вы можете в итоге перехватить все потоки завершения ввода-вывода из пула потоков и серьезно снизить производительность, вплоть до полной блокировки ввода-вывода. Поэтому не отправляйте ACK синхронно из асинхронного обратного вызова.
  • протоколы и форматы, использующие преамбулы, проще в управлении, чем те, которые используют терминаторы. То есть. запишите длину сообщения в заголовке сообщения фиксированного размера, в отличие от обнаружения терминатора \ 0x13. Конечно, это применимо, если протокол находится под вашим контролем, чтобы начать с.

Что касается вашего вопроса, вы не указали, находится ли тот же код, который вы разместили, на стороне клиента.

0 голосов
/ 16 февраля 2010

Вы установили свойство NoDelay в сокете на true? Если установлено значение false (по умолчанию), данные буферизуются до 200 миллисекунд перед отправкой. Причина состоит в том, чтобы уменьшить сетевой трафик, ограничивая количество отправляемых пакетов. Установка NoDelay на true заставит данные отправляться раньше.

0 голосов
/ 16 февраля 2010

попробуйте установить опцию сокета TCP_NODELAY

0 голосов
/ 16 февраля 2010

Как долго вы даете это? Сетевой стек может буферизоваться, и это может задержать передачу. От MSDN :

Для повышения эффективности сети основная система может задержать передача до значительной Количество исходящих данных собирается. Успешное завершение отправки Метод означает, что основной Система имела место для буферизации данные для отправки по сети.

Возможно, вы захотите попробовать сброс, используя метод IOControl .

редактировать

На самом деле, сброс IOControl уничтожит буфер. Возможно, вы захотите проверить проблему Two Generals Problem , чтобы увидеть, будут ли в вашем протоколе некоторые присущие проблемы.

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