Данные не были правильно получены TcpClient - PullRequest
2 голосов
/ 12 марта 2011

У меня есть клиент-серверная программа.

Я отправляю данные вот так:

private void Sender(string s,TcpClient sock)
{
   try
   {
      byte[] buffer = Encoding.UTF8.GetBytes(s);
      sock.Client.Send(buffer);
   }catch{}
}

и на стороне клиента, получая так:

byte[] buffer = new byte[PacketSize];
int size = client.Client.Receive(buffer);
String request = Encoding.UTF8.GetString(buffer, 0, size);

Проблема в том, что данные не всегда принимаются полностью, иногда это только часть того, что я отправил. PacketSize равно 10240, что больше байтов, которые я отправляю. Я также установил SendBufferSize и ReceiveBufferSize с обеих сторон.

Хуже всего то, что иногда данные полностью принимаются!

В чем может быть проблема?

1 Ответ

7 голосов
/ 12 марта 2011

Значение size, возвращаемое TcpClient.Receive, не совпадает с длиной буферизованной строки, которую вы отправили.Это связано с тем, что нет гарантии, что при вызове Receive один раз вы получите обратно все данные, которые вы отправили с Send вызовом.Такое поведение является неотъемлемой частью работы TCP (это потоковый, а не протокол данных на основе сообщений).

Вы не можете решить проблему, используя большие буферы, поскольку предоставленные вами буферы могут только предел количество данных, которое Receive возвращает.Даже если вы предоставляете буфер размером 1 МБ и данные для чтения 1 МБ, Receive может законно вернуть любое количество байтов (даже всего 1).

Что вам нужно сделать, это убедиться, что вы буферизироваливсе данные перед звонком Encoding.GetString.Чтобы сделать это, вам нужно знать, сколько данных в первую очередь.Таким образом, по крайней мере, вам нужно записать длину байтов строки при отправке:

  byte[] buffer = Encoding.UTF8.GetBytes(s);
  byte[] length = BitConverter.GetBytes(buffer.Length);
  sock.Client.Send(length);
  sock.Client.Send(buffer);

При получении вы сначала прочитаете длину (которая имеет известный фиксированный размер: 4 байта), а затемначинайте буферизовывать оставшиеся данные во временном буфере до тех пор, пока у вас не будет length байт (это может занять любое количество вызовов Receive, поэтому вам понадобится цикл while).Только тогда вы можете позвонить Encoding.GetString и вернуть исходную строку.

Объяснение поведения, которое вы наблюдаете:

Даже если сетевой стек ОС делает довольномного никаких гарантий, на практике обычно выдает данные, которые приносит один пакет TCP с одним вызовом Receive.Поскольку MTU (максимальный размер пакета) для TCP допускает около 1500 байт для полезной нагрузки, простой код будет работать нормально, пока строки меньше этого размера.Более того, он будет разбит на несколько пакетов, и один Receive вернет только часть данных.

...