Поведение отправки UDP после connect()

#include <stdio.h>  
#include <errno.h>  
#include <stdlib.h>  
#include <string.h>  
#include <sys/types.h>  
#include <sys/socket.h>  
#include <netinet/in.h>  
#include <arpa/inet.h>  

int main()  
{    
    struct sockaddr_in addr;  
    int fd, cnt,ret;  
    char ch = 'y',msg[] ="How are you";  

    if ((fd=socket(AF_INET,SOCK_DGRAM,0)) < 0) {  
        printf("Error: socket");  
        exit(1);  
    }
    printf("\nDone socket\n");  

    /* set up destination address */  
    memset(&addr,0,sizeof(addr));  
    addr.sin_family=AF_INET;  
    addr.sin_addr.s_addr=inet_addr("128.88.143.113");  
    addr.sin_port=htons(9090);  

    ret=connect(fd,(struct sockaddr *)&addr,sizeof(addr));  
    perror("Connect:");  

    while(ch == 'y'){  
        cnt =  send(fd,msg,sizeof(msg),0);  
        if(cnt < 0)  
        perror("send:");  
        printf("\nNumber of bytes sent = %d , \n",cnt);  
        printf("Continue (y/n)\n");  
        scanf(" %c",&ch);  

     }

     return 0;  
}  

Приведенный выше код скомпилирован для запуска на компьютере с Linux.

Предположим, что приведенный выше код отправляет данные на компьютер по IP-адресу 128.88.143.113, UDP-сокет не привязан к порту 9090 в 128.88.143.113,

в while цикл, первый вызов send() успешно (пакет на самом деле выходит на провод; проверил его с помощью trace) и второй send() не удается с Connection refused, third send() успех и четвертый провал и так далее.

Я подозреваю, что после первого send() стек получает сообщение об ошибке ICMP (замечено в tcpdump на машине Linux), которая сохраняется в структуре сокета. Второй send() не удается увидеть эту ошибку, и на самом деле пакет не отправлен. Второй send() также очищает ошибку в структуре сокета. Поэтому третий send() успех и четвертый провал и так далее.

Вопросы:

  1. Это предположение верно?
  2. Какое должно быть правильное поведение? Существует ли какой-либо стандарт RFC, определяющий такое поведение?
  3. Поскольку UDP не поддерживает состояние соединения, не каждый send() добиться успеха?

5 ответов

Согласно справочной странице по Linux для UDP:

Все фатальные ошибки будут переданы пользователю в виде возврата ошибки, даже если сокет не подключен. Это включает в себя асинхронные ошибки, полученные из сети. Вы можете получить ошибку для более раннего пакета, который был отправлен в тот же сокет. Это поведение отличается от многих других реализаций сокетов BSD, которые не пропускают никаких ошибок, если сокет не подключен. Поведение Linux предписано RFC 1122.

В частности, RFC (4.1.3.3) гласит:

UDP ДОЛЖЕН передавать на прикладной уровень все сообщения об ошибках ICMP, которые он получает от IP-уровня. Концептуально, по крайней мере, это может быть достигнуто с помощью вызова процедуры ERROR_REPORT

Ваша гипотеза верна. Справочная страница Linux udp(7) описывает ситуацию следующим образом:

Все фатальные ошибки будут переданы пользователю в виде возврата ошибки, даже если сокет не подключен. Это включает в себя асинхронные ошибки, полученные из сети. Вы можете получить ошибку для более раннего пакета, который был отправлен в тот же сокет.
Это поведение отличается от многих других реализаций сокетов BSD, которые не пропускают никаких ошибок, если сокет не подключен. Поведение Linux предписано RFC 1122.

Когда IP_RECVERR опция включена, все ошибки хранятся в очереди ошибок сокета и могут быть получены recvmsg(2) с MSG_ERRQUEUE флаг установлен.

Было бы интересно сравнить эквивалентный код, используя sendto() скорее, чем connect() а также send(),

Является ли показанный код таким же образом, если вы оставляете промежуток времени между каждой отправкой, то есть состояние ошибки ICMP сохраняется в сокете в течение некоторого периода времени, или он все равно будет сбой во второй раз send() если вы оставили это, скажем, час?

Я ожидаю, что ваше предположение верно, сетевой стек пытается быть умным. Там нет другого момента, когда он мог бы вернуть "соединение отказано", так как ничего не отправляется, когда connect() Выдается вызов, он просто сохраняет указанный адрес, так что сокет "логически" подключен, и вызывает send() может тогда работать.

Для начала на другом конце, если вы подключите сокет UDP, вы можете собирать ошибки при следующей отправке. Если вы не хотите этого, не подключайтесь!

У меня такая же проблема; и это связано с тем, что очередь сообщений udp заполняется, если никто не слушает и не очищает очередь.

Другие вопросы по тегам