10
votes

Pourquoi ne pas voir msg_eor pour sock_seqpacket sur Linux?

J'ai deux processus qui communiquent sur une paire de sockets créés avec SocketPair () et Sock_Seqpacket. Comme ceci:

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;
}


1 commentaires

Je ne lis pas trop trop, comme on peut le voir par moi pour être confus quant à la méthode que je lisais, envoyez et recevez-les et que je reçois, partagez beaucoup de code pouvant être pris en charge. Essayez-vous d'envoyer des données de bande? Les sockets et les tuyaux IP ne peuvent pas envoyer de données de bande (à l'exception de l'EOF lorsque la connexion est fermée), je suppose que les sockets UNIX sont les mêmes.


3 Réponses :


3
votes

Ce n'est pas ce que msg_eor est pour.

N'oubliez pas que l'API de sockets est une abstraction sur un certain nombre de protocoles différents, notamment des prises de fichiers de fichiers UNIX, des pablesPairs, TCP, UDP et de nombreux protocoles de réseau différents, y compris X.25 et certains entièrement oubliés.

msg_eor consiste à signaler la fin des enregistrements où cela est logique pour le protocole sous-jacent. C'est à dire. Il est de passer un message à la couche suivante sur laquelle "ceci complète un enregistrement". Cela peut affecter par exemple la mise en mémoire tampon, provoquant le rinçage d'un tampon. Mais si le protocole lui-même n'a pas de concept d'un "record", il n'y a aucune raison d'attendre que le drapeau soit propagé.

Deuxièmement, si vous utilisez Seqpacket, vous DOIT Lisez l'ensemble du message à la fois. Si vous ne le faites pas, le reste sera jeté. C'est documenté. En particulier, msg_eor est pas un drapeau pour vous dire que ceci est la dernière partie du paquet.

Conseil: vous écrivez évidemment une version non-Seqpacket pour une utilisation sur MacOS. Je vous suggère de jeter la version Seqpacket car il ne va que doubler le fardeau de maintenance et de codage. Sock_stream convient parfaitement à toutes les plateformes.


3 commentaires

Je ne parle pas d'Eor, mais msg_eor. Voir la page d'homme pour SendMeng (2). J'ai pris un petit regard sur la source et un seul pilote définissait le drapeau msg_eor (je ne me souviens plus que ...).


Édité pour corriger d'Eor à msg_eor - je venais juste d'abréger, mais qui était évidemment déroutant, désolé.


(Je sais que cette réponse est vieille, mais c'est toujours problématique.) Man 2 RECVMSG SUR KUBUNTU 14.04 COURS: "Le champ MSG_Flags dans la MSGHDR est défini sur le retour de RECVMSG (). Il peut contenir plusieurs Drapeaux: msg_eor indique la fin de l'enregistrement; les données renvoyées ont terminé un enregistrement (généralement utilisé avec des sockets de type sock_seqpacket). ... "Ma compréhension est donc que RECVMSG est documenté pour définir msg_eor après avoir reçu la fin. de disque, mais cela ne sera pas. J'utilise également pf_unix (aka pf_local) avec Sock_Seqpacket.



7
votes

avec sockets de domaine SOCK_SEQPACKET UNIX Le seul moyen pour le message à couper le court est si le tampon que vous donnez à RECVMSG () n'est pas assez grand (et dans ce cas, vous obtiendrez msg_trunc).

POSIX dit que Sock_Seqpacket sockets doit définir msg_eor à la fin d'un enregistrement, mais les prises de domaine Linux Unix ne le font pas.

(REF: POSIX 2008 2.10.10 Selon Sock_Seqpacket DOIT Priser des enregistrements et 2.10.6 indique que les limites d'enregistrement sont visibles au récepteur via le drapeau msg_eor.)

Quel moyen "record" pour un protocole donné est à la mise en œuvre de définir.

Si Linux a implémenté msg_eor pour les sockets de domaine UNIX, je pense que la seule manière judicieuse serait de dire que chaque paquet était un enregistrement en soi, puis définit msg_eor (ou peut-être toujours le régler toujours lors de la définition de MSG_TRunch), donc De toute façon, cela ne serait pas informatif.


1 commentaires

Vous êtes absolument correct et effectivement (comme je l'ai dit dans un autre commentaire), un seul pilote NIC définit ce drapeau ou l'a fait quand j'ai regardé. Apparemment, cela se produit dans le conducteur et non dans la couche de protocole ...



3
votes

Lorsque vous lisez les documents, sock_seqpacket diffère de sock_stream de deux manières distinctes. Premièrement -

Chemin de transmission de données à deux voies séquencé, fiable et bidirectionnel pour datagrammes de longueur maximale fixe; Un consommateur est tenu de lire un paquet entier avec chaque appel de système d'entrée .

- socket (2) du projet Linux Manpages

aka

Pour les prises basées sur des messages, telles que SOCK_DGRAM et SOCK_SEQPACKET, l'ensemble du message doit être lu en une seule opération. Si un message est trop long pour s'adapter aux mémoires tampons fournis, et msg_peek n'est pas défini dans l'argument des indicateurs, les octets excédentaires doivent être rejetés et msg_trunc doit être défini dans le membre msg_flags de la structure MSGHDR.

- RECVMSG () dans la norme POSIX.

Dans ce sens, il est similaire à sock_dgram .

Deuxièmement, chaque "Datagramme" (Linux) / "Message" (POSIX) porte un drapeau appelé msg_eor .

Cependant, Linux sock_seqpacket pour AF_UNIX ne met pas en œuvre msg_eor . Les documents actuels ne correspondent pas à la réalité: -)


prétendument certains sock_seqpacket Les implémentations font l'autre. Et certains implémentent les deux. Donc, couvre toutes les différentes combinaisons différentes: -)

[1] Les protocoles orientés par paquets utilisent généralement le niveau de paquets se lit avec troncature / rejet Semantitique et no msg_eor. X.25, Bluetooth, IRDA, et les prises de domaine UNIX utilisent de cette manière Sock_Seqpacket.

[2] Les protocoles orientés enregistrés utilisent généralement des lectures de flux d'octets et msg_eor - Pas de visibilité de niveau de paquet, pas de troncature / jupard. Decnet et ISO TP utilisent Sock_Seqpacket de cette façon.

[3] Les hybrides de paquets / records utilisent généralement Sock_Seqpacket avec troncature / Jeter la sémantique sur le niveau du paquet et enregistrer des paquets de terminaison marqué avec msg_eor. SPX et XNS SPP utilisent cette façon Sock_Seqpacket de cette façon.

https://mailarchive.ief.org/arc/msg/tsvwg/9pdzbog1kqdzq2waul5vnajrrrka

Vous avez montré un exemple de paragraphe 1.

paragraphe 2 s'applique également à sock_seqpacket tel que défini pour SCTP . Bien que par défaut, il définit msg_eor sur chaque sendmsg () . L'option de désactiver cela s'appelle sctp_explicit_eor .

paragraphe 3, celui qui est le plus conforme aux documents, semble être le cas le plus obscur.

Et même les documents ne sont pas correctement compatibles avec eux-mêmes.

Le type de socket Sock_Seqpacket est similaire au type Sock_stream et est également axé sur la connexion. La différence entre ces types est que les limites d'enregistrement sont maintenues à l'aide du type Sock_Seqpacket . Un enregistrement peut être envoyé en utilisant une ou plusieurs opérations de sortie et reçue à l'aide d'une ou de plusieurs opérations d'entrée, mais une seule opération ne transfère jamais des parties de plus d'un enregistrement. Les limites d'enregistrement sont visibles au récepteur via le drapeau msg_eor dans les indicateurs de message reçus renvoyés par la fonction RECVMSG (). - Standard POSIX


0 commentaires