2010-08-29 10 views
8

Ho due processi che comunicano su una coppia di socket creati con socketpair() e SOCK_SEQPACKET. Come questo:Perché non vedo MSG_EOR per SOCK_SEQPACKET su linux?

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

A quanto ho capito, mi dovrebbe vedere MSG_EOR negli Stati msg_flags di "struct msghdr" quando si riceve un record SOCK_SEQPACKET. Sto impostando MSG_EOR in sendmsg() per essere certo che il record sia segnato MSG_EOR, ma non lo vedo quando lo ricevo in recvmsg(). Ho persino provato a impostare MSG_EOR nel campo msg_flags prima di inviare il record, ma ciò non ha fatto alcuna differenza.

Penso che dovrei vedere MSG_EOR a meno che il record non sia stato interrotto, ad es. un segnale, ma io no. Perché?

Ho incollato il mio codice di invio e ricezione in basso.

Grazie, 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

Non letto tutto troppo, come si può vedere da me confondendo su quale metodo stavo leggendo, inviando e ricevendo condivido un sacco di codice che potrebbe essere scomposto. Stai cercando di inviare dati fuori banda. Le prese e le pipe IP non possono inviare dati fuori banda (tranne EOF quando la connessione è chiusa), presumo che le prese unix siano le stesse. –

risposta

5

Con SOCK_SEQPACKET sockets di dominio unix l'unico modo per il messaggio da interrompere è se il buffer che si fornisce a recvmsg() non è abbastanza grande (e in tal caso si otterrà MSG_TRUNC).

POSIX dice che i socket SOCK_SEQPACKET devono impostare MSG_EOR alla fine di un record, ma i socket di dominio Linux unix no.

(Rif: POSIX 2008 2.10.10 dice SOCK_SEQPACKET deve supportare dischi e 2.10.6 dice confini discografiche sono visibili al ricevitore tramite la bandiera MSG_EOR.)

Che 'record' significa per un determinato protocollo è fino all'implementazione da definire.

Se Linux implementava MSG_EOR per i socket di dominio unix, penso che l'unico modo ragionevole sarebbe dire che ogni pacchetto era un record in sé e quindi impostare sempre MSG_EOR (o magari impostarlo sempre quando non si imposta MSG_TRUNC), quindi non sarebbe comunque informativo.

+0

Sei assolutamente corretto, e in realtà (come ho detto in un altro commento) solo un driver NIC imposta questo flag, o lo ha fatto quando ho guardato. Apparentemente questo accade molto nel driver e non nel livello di protocollo ... – colding

2

non è quello che è per MSG_EOR.

Ricordare che l'API socket è un'astrazione su un numero di protocolli diversi, inclusi socket del filesystem UNIX, socketpocket, TCP, UDP e molti protocolli di rete diversi, inclusi X.25 e alcuni completamente dimenticati.

MSG_EOR deve segnalare la fine della registrazione dove ciò ha senso per il protocollo sottostante. Cioè è passare un messaggio al livello successivo che "questo completa un record". Questo può influenzare, ad esempio, il buffering, causando lo svuotamento di un buffer. Ma se il protocollo stesso non ha un concetto di "record" non c'è motivo di aspettarsi che il flag venga propagato.

In secondo luogo, se si utilizza SEQPACKET è necessario leggere l'intero messaggio in una volta. Se non lo fai, il resto verrà scartato. Questo è documentato. In particolare, MSG_EOR è non una bandiera per dirti che questa è l'ultima parte del pacchetto.

Avviso: si sta ovviamente scrivendo una versione non SEQPACKET da utilizzare su MacOS. Vi suggerisco di scaricare la versione di SEQPACKET in quanto raddoppierà solo il carico di manutenzione e codifica. SOCK_STREAM va bene per tutte le piattaforme.

+0

Non sto parlando di EOR, ma MSG_EOR. Vedi la pagina man di sendmsg (2). Ho dato una piccola occhiata all'origine e solo un driver stava impostando il flag MSG_EOR (non ricordo quale ...). – colding

+0

Modificato per correggere da EOR a MSG_EOR - Stavo solo abbreviando, ma evidentemente quello era confuso, mi dispiace. – Ben

+0

(So che questa risposta è vecchia, ma è ancora problematica.) 'man 2 recvmsg' su Kubuntu 14.04 restituisce:" Il campo msg_flags nel msghdr è impostato al ritorno di recvmsg(). Può contenere più flag: MSG_EOR indica end-of-record, i dati restituiti completano un record (generalmente utilizzato con socket di tipo SOCK_SEQPACKET). ... " La mia comprensione è quindi che recvmsg è * documentato * per impostare MSG_EOR su aver ricevuto la fine della registrazione, ma doesn'tt. Sto anche usando PF_UNIX (aka PF_LOCAL) con SOCK_SEQPACKET. –

Problemi correlati