Несколько вызовов send() объединяются в один вызов recv()

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

Клиент отправляет строку, за которой следует целое число, используя два разных вызова send(). Эти два данных должны храниться в двух разных переменных на сервере.

Проблема в том, что обе отправленные переменные принимаются при вызове recv(). Таким образом, две строки, отправленные двумя разными send(), объединяются в цепочку и сохраняются в буфере первого recv().

сервер.с:

printf("Incoming connection from client %s:%i accepted\n",inet_ntoa(clientSocketAddress.sin_addr),ntohs(clientSocketAddress.sin_port));


memset(buffer,0,sizeof(buffer));
int sizeofMessage;
if ((recv(clientSocket,buffer,MAXBUFFERSIZE,0)==sizeofMessage)<0)
{
  printf("recv failed.");
  closesocket(serverSocket);
  clearWinsock();
  return EXIT_FAILURE;
}

char* Name=buffer;
printf("Name: %s\n",Name);

if ((recv(clientSocket,buffer,MAXBUFFERSIZE,0))<0)
{
  printf("bind failed.");

  closesocket(serverSocket);
  clearWinsock();
  return EXIT_FAILURE;
}

int integer=ntohs(atoi(buffer));
printf("integer: %i\n",intero);

клиент.с:

if (send(clientSocket,Name,strlen(Name),0)!=strlen(Name))
{
  printf("send failed");

  closesocket(clientSocket);
  clearWinsock();
  return EXIT_FAILURE;
}

printf("client send: %s",Name);

int age=35;
itoa(htons(age),buffer,10);
sizeofBuffer=strlen(buffer);
if (send(clientSocket,buffer,sizeofBuffer,0)!=sizeofBuffer)
{
  printf("bind failed.");

  closesocket(clientSocket);
  clearWinsock();
  return EXIT_FAILURE;
}

Как я могу это исправить? Что я делаю неправильно?


person geraldCelente    schedule 02.07.2014    source источник


Ответы (3)


TCP — это потоковый протокол. Он вообще не осознает никаких границ «сообщения». Он не добавляет такую ​​информацию в зависимости от одиночных вызовов send().

Из-за этих фактов любое количество send()s на стороне отправителя может привести к любому количеству recv()s (вплоть до количества отправленных байтов) на стороне получателя.

Чтобы обойти это поведение, определите и внедрите протокол уровня приложения, чтобы различать разные отправленные «сообщения».

Нельзя полагаться на то, что recv()/send() получит/отправит столько байтов, сколько было сказано получить/отправить этим двум функциям. Крайне необходимо проверить их возвращаемое значение, чтобы узнать, сколько байтов эти функции фактически получили/отправили, и обходить их до тех пор, пока все данные, которые должны были быть получены/отправлены, не будут получены/отправлены.

Например, как это «зацикливание» может быть выполнено

person alk    schedule 02.07.2014

Так работает TCP. Рассматривайте это как поток байтов. Поместите поверх него какой-нибудь базовый протокол - разграничьте сообщения вашего приложения некоторым известным значением байта или добавьте к своим сообщениям поле длины.

Или переключитесь на UDP, который дает вам семантику дейтаграмм, которую вы ищете, если вы можете терпеть/восстанавливаться после случайной потери пакетов.

person Nikolai Fetissov    schedule 02.07.2014
comment
разграничивайте сообщения приложения некоторым известным значением байта просто для уточнения, это называется протоколом, некоторые люди используют что-то вроде этого STXyourmessageETXXOR, где STX (начало) в HEX равно 2, ETX (END) в HEX равно 3, а XOR - побитовая операция ^ для STXyourmessageETX, результат = STX ^ y -> результат ^= u ... где XOR представляет цифру для проверки - person Undefined Behavior; 02.07.2014

Вы можете добавить короткий временной интервал, например, sleep(5) между двумя сообщениями, если время не имеет большого значения в вашем приложении.

person user5280911    schedule 02.12.2019