2010-12-09 12 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; 
} 

El código anterior se compila para ejecutarse en una máquina Linux.UDP enviar comportamiento después de conectar()

Supongamos que el código anterior envía datos a una máquina en la dirección IP 128.88.143.113. Ningún socket UDP está vinculado al puerto 9090 en 128.88.143.113.

En el bucle while, la primera llamada a send() tiene éxito (el paquete pasa en realidad en el cable; comprobó usando trace) y el segundo send() falla con Connection refused. El third send() tiene éxito y el cuarto falla, y así sucesivamente.

Sospecho que después de la primera send() la pila recibe un mensaje de error ICMP (visto en tcpdump en la máquina Linux) que se guarda en la estructura del socket. El segundo send() falla al ver este error y no se envía realmente ningún paquete. El segundo send() también borra el error en la estructura del socket. Por lo tanto, el tercero send() tiene éxito y el cuarto falla, y así sucesivamente.

Preguntas:

  1. es correcta esta suposición?
  2. ¿Cuál debería ser el comportamiento correcto? ¿Existe algún estándar de RFC que defina dicho comportamiento?
  3. Dado que UDP no mantiene ningún estado de conexión, ¿no debería tener éxito cada send()?
+0

Veo un comportamiento similar en una máquina arch Linux cuando se envía a través de una interfaz de alias. ¿Esto fue resuelto? –

Respuesta

1

Para comenzar en el otro extremo, si conecta un socket UDP, puede recopilar errores en el siguiente envío. Si no quieres eso, ¡no te conectes!

2

Sería interesante comparar el código equivalente usando sendto() en lugar de connect() y send().

que hace el código mostrado fallar de la misma manera si se deja un periodo de tiempo entre cada uno envía, es decir, es el estado de error ICMP se mantiene en el zócalo por un período de tiempo, o sería todavía fallar el segundo send() si lo dejaste, digamos, ¿una hora?

Espero que su suposición es correcta, la pila de red está tratando de ser inteligente. No hay otro punto cuando podría devolver 'conexión rechazada' ya que no se envía nada cuando se emite la llamada connect(), simplemente almacena la dirección dada para que el socket esté 'lógicamente' conectado y las llamadas a send() pueden funcionar.

5

Según la linux man page for udp:

Todos los errores fatales serán pasados ​​al usuario como un retorno de error incluso cuando el socket no está conectado. Esto incluye errores asincrónicos recibidos de la red . Puede obtener un error para un paquete anterior que se envió en el mismo socket . Este comportamiento difiere de muchas otras implementaciones BSD socket que no pasan ningún error a menos que el socket esté conectado. El comportamiento de Linux es obligatorio por RFC 1122.

Específicamente la RFC (4.1.3.3) afirma:

UDP debe pasar a la capa de aplicación todo error ICMP mensajes que recibe de la capa IP. Conceptualmente al menos, esto se puede realizar con una llamada ascendente a la ERROR_REPORT rutina

3

Su hipótesis es correcta. La página del manual UDP Linux (7) describe así la situación:

Todos los errores fatales serán pasados ​​ al usuario como un resultado de error incluso cuando el conector no está conectado. Esto incluye los errores asincrónicos recibidos de la red. Puede obtener un error para un paquete anterior que se envió en el mismo socket.
Este comportamiento difiere de muchas otras implementaciones de socket BSD que no pasan ningún error a menos que el socket esté conectado a . El comportamiento de Linux es ordenado por RFC 1122.

Cuando la opción es IP_RECVERR habilitar todos los errores se almacenan en la cola de errores zócalo y pueden ser recibidas por recvmsg(2) con el indicador MSG_ERRQUEUE.

0

que tenían el mismo problema; y se debe al hecho de que la cola de mensajes udp está llena si nadie está escuchando y vaciando la cola.

Cuestiones relacionadas