Последовательные вызовы recvfrom () теряют данные? - PullRequest
7 голосов
/ 24 марта 2009

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

У меня такой вопрос - ну, рассмотрим такой сценарий:

  1. Отправитель имеет (например) 12 байтов данных для отправки. Таким образом, отправитель выполняет этот вызов:

    sendto(fd, &buf, 12, 0, (struct sockaddr *)&cliaddr,sizeof(cliaddr));
    

    Это отправляет 12 байтов данных ненадежным способом. Первые 4 байта этих данных являются полем «длина сообщения». В этом случае первые 4 байта могут иметь значение 0x0000000C

  2. Получатель хочет прочитать первые 4 байта, используя recvfrom (). Видя, что размер сегмента составляет 12 байтов, он хочет прочитать оставшиеся 8 байтов. Таким образом, получатель может выглядеть так:

    /* read the segment size */
    recvfrom(sockfd,&buf,4,0,(struct sockaddr *)&cliaddr,&len);
    
    /* do some arithmetic, use bzero(), etc */
    
    /* read the rest of the data */
    recvfrom(sockfd,&buf,8,0,(struct sockaddr *)&cliaddr,&len);
    

Когда я выполняю этот код, я могу получить первые 4 байта без проблем. Но когда я пытаюсь получить оставшиеся данные, эти данные, похоже, теряются. В моем выводе я получаю мусор - похоже, что некоторая часть следующих 12 байтов отправителя отправлена ​​() - ing.

Это ожидаемое поведение? То есть, если один вызов recvfrom () не читает все отправленные данные, разве не гарантируется, что эти данные (оставшиеся 8 байтов) мне доступны?

Кажется, что стандартный метод отправки заголовка сегмента (включая его размер), за которым следует полезная нагрузка, не работает. Означает ли это, что мне нужно отправить 2 отдельных сегмента - один, содержащий только информацию заголовка, а затем второй сегмент с полезной нагрузкой? Или я просто неправильно использую эти системные вызовы (или я пропускаю флаг или setsockopt ()?)

Ответы [ 2 ]

8 голосов
/ 24 марта 2009

Из справочной страницы recv (2):

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

Вот как это происходит с тобой.

Вы должны иметь буфер с максимальным размером сообщения и прочитать это количество. Вы будете читать только одну дейтаграмму, и длина будет возвращена. Затем вы можете проанализировать длину в начале буфера и сравнить ее с возвращенной функцией recvfrom (2).

2 голосов
/ 10 ноября 2009

Другой метод - сделать фиктивную запись с флагом MSG_PEEK. Хотя возвращаемый размер совпадает с вашим размером буфера (или больше), получите больший буфер и попробуйте снова. Затем снова выполните recvfrom (без флага MSG_PEEK), чтобы удалить сообщение из буфера UDP.

Но, конечно, это довольно неэффективно и не должно быть сделано, когда вы можете просто определить максимальный размер пакета.

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