Более быстрый способ общения с помощью TcpClient? - PullRequest
8 голосов
/ 25 мая 2011

Я пишу клиент-серверное приложение на C #, и все идет отлично. На данный момент все работает, и все это довольно надежно. Моя проблема в том, что у меня возникают некоторые задержки при отправке пакетов по соединению.

На стороне клиента я делаю это:

NetworkStream ns = tcpClient.GetStream();

// Send packet

byte[] sizePacket = BitConverter.GetBytes(request.Length);
byte[] requestWithHeader = new byte[sizePacket.Length + request.Length];
sizePacket.CopyTo(requestWithHeader, 0);
request.CopyTo(requestWithHeader, sizePacket.Length);

ns.Write(requestWithHeader, 0, requestWithHeader.Length);

// Receive response

ns.Read(sizePacket, 0, sizePacket.Length);
int responseLength = BitConverter.ToInt32(sizePacket, 0);
byte[] response = new byte[responseLength];

int bytesReceived = 0;
while (bytesReceived < responseLength)
{
  int bytesRead = ns.Read(response, bytesReceived, responseLength - bytesReceived);
  bytesReceived += bytesRead;
}

(Не обращая внимания на перехват исключений и т. Д.) Сервер делает обратное, то есть блокирует NetworkStream.Read () до тех пор, пока не получит полный запрос, затем обрабатывает его и отправляет ответ с помощью Write ().

Необработанная скорость записи () / чтения () не является проблемой (т. Е. Отправка больших пакетов происходит быстро), но отправка нескольких небольших пакетов, один за другим, без закрытия соединения, может быть очень медленной (задержки 50-100 мс). Странно то, что эти задержки обнаруживаются при подключении к локальной сети с типичным временем пинга <1 мс, но они не происходят, если сервер работает на локальном хосте, даже если время пинга будет фактически одинаковым (по крайней мере, разница не должна быть порядка 100 мс). Это имело бы смысл для меня, если бы я повторно открывал соединение для каждого пакета, вызывая много рукопожатий, но я не. Это как если бы сервер, переходящий в состояние ожидания, выбрасывает его из синхронизации с клиентом, а затем немного спотыкается, восстанавливая то, что по сути является потерянным соединением. </p>

Итак, я делаю это неправильно? Есть ли способ синхронизировать соединение между TcpServer и TcpClient, чтобы сервер всегда был готов к приему данных? (И наоборот: иногда обработка запроса от клиента занимает несколько мс, а затем клиент, похоже, не готов получить ответ от сервера, пока у него не будет несколько минут для пробуждения после блокировки на Read () .)

1 Ответ

7 голосов
/ 26 мая 2011

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

ns.Write(sizePacket, 0, sizePacket.Length);
ns.Write(response, 0, response.Length);

Который я изменил на это:

// ... concatenate sizePacket and response into one array, same as client code above
ns.Write(responseWithHeader, 0, responseWithHeader.Length);

А теперь задержка полностью прошла или, по крайней мере, больше не измеряется в миллисекундах. Это что-то вроде ускорения в 100 раз. \ О /

Это все еще странно, потому что он записывает в сокет точно те же данные, что и раньше, поэтому я предполагаю, что сокет получает некоторые секретные метаданные во время операции записи, которые затем каким-то образом передаются на удаленный сокет, что может интерпретировать их как возможность вздремнуть. Либо это, либо первая запись переводит сокет в режим приема, вызывая его отключение, когда его затем просят отправить еще раз, прежде чем он что-либо получит.

Я полагаю, что подразумевается, что весь этот пример кода, который вы находите, лежит вокруг, который показывает, как записывать и читать из сокетов в чанках фиксированного размера (часто предшествует один int, описывающий размер пакета, который должен следовать так же, как и моя первая версия), не могу не упомянуть, что за это очень сильно снижается производительность.

...