2010-12-09 2 views
8
#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.UDP отправить поведение после подключения()

Предположим, что приведенный выше код отправляет данные на машину по 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() преуспеть?
+0

Я вижу подобное поведение на машине с архитектурой при отправке через интерфейс псевдонима. Было ли это разрешено? –

ответ

1

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

2

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

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

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

5

Согласно linux man page for udp:

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

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

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

3

Ваша гипотеза верна. Человек страница Linux УДП (7) описывает ситуацию следующим образом:

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

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

0

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

Смежные вопросы