c ++ Сокет выбора и получения проблемы - PullRequest
2 голосов
/ 01 февраля 2010

Ниже приведен фрагмент кода, с которым я столкнулся при программировании сокетов. Здесь после выберите вызов. Если я не помещаю спящий режим в строку 9, в Windows XP в строке 11 принимается 1 байт (вместо этого 4 байта отправляются с сервера как целое число), когда я проверяю xmlSize, он установлен в 0. Поскольку iResult равен 1, выполнение продолжается, и в строке 15 второй вызов вызывается с xmlSize = 0, а iResult устанавливается в 0 и далее, поскольку соединение iResult = 0 закрыто.

Но в Windows 7 этого не произошло, программа успешно прочитала 4 байта и продолжила нормальное выполнение. На XP однако я поставил сон (я только что сделал это), и это сработало, но почему ??

Какой недостаток в этом коде?

1   while(is_running())
2   {
3       FD_ZERO(&readfds);
4       FD_SET(server_socket, &readfds);
5       iResult = select(server_socket+1, &readfds, NULL, NULL, &tv);
6       if  (!(iResult != SOCKET_ERROR && FD_ISSET(server_socket, &readfds) )) {
7           continue;
8       }
9       Sleep(500); // This Sleep is not required on Windows 7, but is required on 10 XP but WHY? 
11      iResult = recv(server_socket, (char *)&xmlSize, sizeof(xmlSize), 0);
12      xmlSize = htonl(xmlSize);
13      if ( iResult > 0 ){
13          printf("Bytes received: %d, XML Size:%d", iResult, xmlSize);
14          
15          iResult = recv(server_socket, xml, xmlSize, 0);
16          if ( iResult > 0 ){
17              xml[xmlSize] = '\0';
18              printf("Bytes received: %d", iResult);              
19              operation_connection(xml);
20          }
21          else if ( iResult == 0 ){
22              printf(LL_INTERR, CLOG("Connection closed"));
23              break;
24          }
25          else{
26              printf("recv failed with error: %d", WSAGetLastError());
27              break;
28          }
29      }
30      else if ( iResult == 0 ){
31          printf(LL_INTERR, CLOG("Connection closed"));   
32          break;
33      }
34      else{
35          printf("recv failed with error: %d", WSAGetLastError());
36          break;
37      }
38  }

Ответы [ 2 ]

7 голосов
/ 01 февраля 2010

Если это сокет TCP, вам все равно. Сокет доставляет поток , он никак не соответствует размеру оригинального write() s на другом конце.

Он может выдавать мегабайт в виде одного миллиона 1-байтовых read() с, или одного 1 МБ, или любой комбинации между ними.

Если вы зависите от размера «кусков» доставленных данных для TCP-соединения, вы делаете это неправильно.

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

  • Используйте некоторую другую последовательность байтов, возможно, ASCII 0x1E, «разделитель записей».
  • Escape CR + LF, когда они содержатся в сообщении, чтобы «простые» работали как разделители. Это было бы лучшим решением, если ваш протокол «хочет» быть текстовым.

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

3 голосов
/ 01 февраля 2010

См. Этот другой вопрос SO для ответа и пример кода:

Чтение из сокета: гарантированно получено хотя бы x байтов?

...