2010-08-29 18 views
8

Tengo dos procesos que se comunican a través de un par de sockets creados con socketpair() y SOCK_SEQPACKET. De esta manera:¿Por qué no veo MSG_EOR para SOCK_SEQPACKET en Linux?

int ipc_sockets[2]; 
socketpair(PF_LOCAL, SOCK_SEQPACKET, 0, ipc_sockets); 

Como lo entiendo, yo debería ver MSG_EOR en el miembro msg_flags de "estructura msghdr" cuando se recibe un registro SOCK_SEQPACKET. Estoy configurando MSG_EOR en sendmsg() para asegurarme de que el registro está marcado como MSG_EOR, pero no lo veo cuando recibo en recvmsg(). Incluso he tratado de establecer MSG_EOR en el campo msg_flags antes de enviar el registro, pero eso no hizo ninguna diferencia.

Creo que debería ver MSG_EOR, a menos que el registro se haya interrumpido, por ej. una señal, pero yo no. ¿Porqué es eso?

He pegado mi código de envío y recepción a continuación.

Gracias, Jules

int 
send_fd(int fd, 
     void *data, 
     const uint32_t len, 
     int fd_to_send, 
     uint32_t * const bytes_sent) 
{ 
    ssize_t n; 
    struct msghdr msg; 
    struct iovec iov; 

    memset(&msg, 0, sizeof(struct msghdr)); 
    memset(&iov, 0, sizeof(struct iovec)); 

#ifdef HAVE_MSGHDR_MSG_CONTROL 
    union { 
     struct cmsghdr cm; 
     char control[CMSG_SPACE_SIZEOF_INT]; 
    } control_un; 
    struct cmsghdr *cmptr; 

    msg.msg_control = control_un.control; 
    msg.msg_controllen = sizeof(control_un.control); 
    memset(msg.msg_control, 0, sizeof(control_un.control)); 

    cmptr = CMSG_FIRSTHDR(&msg); 
    cmptr->cmsg_len = CMSG_LEN(sizeof(int)); 
    cmptr->cmsg_level = SOL_SOCKET; 
    cmptr->cmsg_type = SCM_RIGHTS; 
    *((int *) CMSG_DATA(cmptr)) = fd_to_send; 
#else 
    msg.msg_accrights = (caddr_t) &fd_to_send; 
    msg.msg_accrightslen = sizeof(int); 
#endif 
    msg.msg_name = NULL; 
    msg.msg_namelen = 0; 

    iov.iov_base = data; 
    iov.iov_len = len; 
    msg.msg_iov = &iov; 
    msg.msg_iovlen = 1; 

#ifdef __linux__ 
    msg.msg_flags = MSG_EOR; 
    n = sendmsg(fd, &msg, MSG_EOR); 
#elif defined __APPLE__ 
    n = sendmsg(fd, &msg, 0); /* MSG_EOR is not supported on Mac                                           
           * OS X due to lack of                                              
           * SOCK_SEQPACKET support on                                            
           * socketpair() */ 
#endif 
    switch (n) { 
    case EMSGSIZE: 
     return EMSGSIZE; 
    case -1: 
     return 1; 
    default: 
     *bytes_sent = n; 
    } 

    return 0; 
} 

int 
recv_fd(int fd, 
     void *buf, 
     const uint32_t len, 
     int *recvfd, 
     uint32_t * const bytes_recv) 
{ 
    struct msghdr msg; 
    struct iovec iov; 
    ssize_t n = 0; 
#ifndef HAVE_MSGHDR_MSG_CONTROL 
    int newfd; 
#endif 
    memset(&msg, 0, sizeof(struct msghdr)); 
    memset(&iov, 0, sizeof(struct iovec)); 

#ifdef HAVE_MSGHDR_MSG_CONTROL 
    union { 
     struct cmsghdr cm; 
     char control[CMSG_SPACE_SIZEOF_INT]; 
    } control_un; 
    struct cmsghdr *cmptr; 

    msg.msg_control = control_un.control; 
    msg.msg_controllen = sizeof(control_un.control); 
    memset(msg.msg_control, 0, sizeof(control_un.control)); 
#else 
    msg.msg_accrights = (caddr_t) &newfd; 
    msg.msg_accrightslen = sizeof(int); 
#endif 
    msg.msg_name = NULL; 
    msg.msg_namelen = 0; 

    iov.iov_base = buf; 
    iov.iov_len = len; 
    msg.msg_iov = &iov; 
    msg.msg_iovlen = 1; 

    if (recvfd) 
     *recvfd = -1; 

    n = recvmsg(fd, &msg, 0); 
    if (msg.msg_flags) { // <== I should see MSG_EOR here if the entire record was received 
     return 1; 
    } 
    if (bytes_recv) 
     *bytes_recv = n; 
    switch (n) { 
    case 0: 
     *bytes_recv = 0; 
     return 0; 
    case -1: 
     return 1; 
    default: 
     break; 
    } 

#ifdef HAVE_MSGHDR_MSG_CONTROL 
    if ((NULL != (cmptr = CMSG_FIRSTHDR(&msg))) 
     && cmptr->cmsg_len == CMSG_LEN(sizeof(int))) { 
     if (SOL_SOCKET != cmptr->cmsg_level) { 
      return 0; 
     } 
     if (SCM_RIGHTS != cmptr->cmsg_type) { 
      return 0; 
     } 
     if (recvfd) 
      *recvfd = *((int *) CMSG_DATA(cmptr)); 
    } 
#else 
    if (recvfd && (sizeof(int) == msg.msg_accrightslen)) 
     *recvfd = newfd; 
#endif 
    return 0; 
} 
+0

No lo leí demasiado, como puedo ver confundiéndome con respecto a qué método estaba leyendo, enviar y recibir comparten un gran número de códigos que se pueden descartar. ¿Estás tratando de enviar datos fuera de banda? Los sockets y las tomas IP no pueden enviar datos fuera de banda (excepto EOF cuando la conexión está cerrada), supongo que los sockets unix son los mismos. –

Respuesta

5

Con sockets de dominio SOCK_SEQPACKET Unix la única forma de acortar el mensaje es si el búfer que le da a recvmsg() no es lo suficientemente grande (y en ese caso obtendrá MSG_TRUNC).

POSIX dice que los sockets SOCK_SEQPACKET deben establecer MSG_EOR al final de un registro, pero los sockets de dominio de Linux no lo hacen.

(Ref: 2008 2.10.10 POSIX dice SOCK_SEQPACKET debe ser compatible con los registros y 2.10.6 dice los límites de registro son visibles para el receptor a través de la bandera MSG_EOR.)

Qué significa un 'record' para un protocolo dado depende de la implementación para definir.

Si Linux implementó MSG_EOR para sockets de dominio unix, creo que la única manera sensata sería decir que cada paquete era un registro en sí mismo, así que siempre configure MSG_EOR (o tal vez siempre configúrelo cuando no configure MSG_TRUNC), por lo que no sería informativo de todos modos.

+0

Usted es absolutamente correcto, y de hecho (como ya he dicho en otro comentario) sólo un controlador NIC establece este indicador, o así lo hice cuando me veía. Aparentemente, esto ocurre en el controlador y no en la capa de protocolo ... – colding

2

Eso no es lo que es para MSG_EOR.

Recuerde que la API Sockets es una abstracción de varios protocolos diferentes, incluidos sockets de sistema de archivos UNIX, pares de socket, TCP, UDP y muchos protocolos de red diferentes, incluyendo X.25 y algunos completamente olvidados.

MSG_EOR indica el final del registro donde eso tiene sentido para el protocolo subyacente. Es decir. es pasar un mensaje a la siguiente capa que "esto completa un registro". Esto puede afectar, por ejemplo, el almacenamiento en búfer, causando el enjuague de un búfer. Pero si el protocolo en sí no tiene un concepto de "registro", no hay razón para esperar que la bandera se propague.

En segundo lugar, si utiliza SEQPACKET usted debe leer todo el mensaje a la vez. Si no lo haces, el resto será descartado. Eso está documentado. En particular, MSG_EOR es no una bandera para indicarle que esta es la última parte del paquete.

Asesoramiento: Obviamente está escribiendo una versión que no es SEQPACKET para utilizar en MacOS. Sugiero que elimine la versión de SEQPACKET ya que solo duplicará la carga de mantenimiento y codificación. SOCK_STREAM está bien para todas las plataformas.

+0

No estoy hablando de EOR, sino de MSG_EOR. Consulte la página man para sendmsg (2). Eché un vistazo a la fuente y solo un controlador estaba configurando la bandera MSG_EOR (no recuerdo qué ...). – colding

+0

Editado para corregir de EOR a MSG_EOR - Estaba abreviar, pero es evidente que era confuso, lo siento. – Ben

+0

(Sé que esta respuesta es antigua, pero sigue siendo problemática.) 'man 2 recvmsg' en Kubuntu 14.04 cede:" El campo msg_flags en el msghdr se establece en el retorno de recvmsg(). Puede contener varios indicadores: MSG_EOR indica el final del registro; los datos devueltos completaron un registro (generalmente utilizado con conectores de tipo SOCK_SEQPACKET). ... " Por lo tanto, entiendo que recvmsg está * documentado * para configurar MSG_EOR al recibir el final del registro, pero doesn'tt. También estoy usando PF_UNIX (también conocido como PF_LOCAL) con SOCK_SEQPACKET. –

Cuestiones relacionadas