Почему я не вижу MSG_EOR для SOCK_SEQPACKET в Linux? - PullRequest
9 голосов
/ 29 августа 2010

У меня есть два процесса, которые взаимодействуют через пару сокетов, созданных с помощью socketpair () и SOCK_SEQPACKET.Например:

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

Насколько я понимаю, я должен видеть MSG_EOR в элементе msg_flags "struct msghdr" при получении записи SOCK_SEQPACKET.Я устанавливаю MSG_EOR в sendmsg (), чтобы быть уверенным, что запись помечена MSG_EOR, но я не вижу ее при получении в recvmsg ().Я даже пытался установить MSG_EOR в поле msg_flags перед отправкой записи, но это не имело никакого значения.

Я думаю, что должен увидеть MSG_EOR, если запись не была прервана, например, сигналом,Я не буду.Почему это так?

Я вставил код отправки и получения ниже.

Спасибо, Джулс

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

Ответы [ 3 ]

5 голосов
/ 22 мая 2011

С сокетами домена unix SOCK_SEQPACKET единственный способ обрезать сообщение - это если буфер, который вы даете recvmsg (), недостаточно велик (и в этом случае вы получите MSG_TRUNC).

POSIX говорит, что сокеты SOCK_SEQPACKET должны устанавливать MSG_EOR в конце записи, а сокеты домена Linux unix - нет.

(Ссылки: POSIX 2008 2.10.10 говорит, что SOCK_SEQPACKET должен поддерживать записи, а 2.10.6 говорит, что границы записи видны получателю через флаг MSG_EOR.)

Что означает «запись» для данного протокола, зависит от реализации, чтобы определить.

Если Linux действительно реализовал MSG_EOR для сокетов домена unix, я думаю, что единственным разумным способом было бы сказать, что каждый пакет был записью сам по себе, и поэтому всегда устанавливайте MSG_EOR (или, возможно, всегда устанавливайте его, когда не устанавливаете MSG_TRUNC), в любом случае это не было бы информативно.

2 голосов
/ 15 апреля 2011

Это не то, для чего предназначен MSG_EOR.

Помните, что API сокетов является абстракцией для ряда различных протоколов, в том числе сокетов файловой системы UNIX, пар сокетов, TCP, UDP и многих других сетевых протоколов, включая X.25 и некоторые полностью забытые.

MSG_EOR должен сигнализировать об окончании записи, если это имеет смысл для базового протокола.Т.е. это передать сообщение следующему слою вниз, что «это завершает запись».Это может повлиять, например, на буферизацию, вызывая сброс буфера.Но если сам протокол не имеет понятия «запись», нет никаких оснований ожидать распространения флага.

Во-вторых, если вы используете SEQPACKET, вы должны прочитать всесообщение сразу.Если вы этого не сделаете, остаток будет сброшен.Это задокументировано.В частности, MSG_EOR является , а не флагом, указывающим, что это последняя часть пакета.

Совет: Вы, очевидно, пишете версию, отличную от SEQPACKET.для использования на MacOS.Я предлагаю вам сбросить версию SEQPACKET, поскольку она только удвоит затраты на обслуживание и кодирование.SOCK_STREAM подходит для всех платформ.

0 голосов
/ 14 февраля 2019

Когда вы читаете документы, SOCK_SEQPACKET отличается от SOCK_STREAM двумя различными способами. Во-первых -

Последовательный, надежный двухсторонний канал передачи данных на основе соединений для дейтаграмм фиксированной максимальной длины; потребитель должен прочитать весь пакет с каждым входным системным вызовом .

- socket(2) из проекта manpages в Linux

ака

Для сокетов на основе сообщений, таких как SOCK_DGRAM и SOCK_SEQPACKET, все сообщение должно быть прочитано за одну операцию. Если сообщение слишком длинное, чтобы поместиться в предоставленные буферы, и MSG_PEEK не установлен в аргументе flags, лишние байты должны быть отброшены, а MSG_TRUNC должен быть установлен в элементе msg_flags структуры msghdr.

- recvmsg() в стандарте POSIX.

В этом смысле он похож на SOCK_DGRAM.

Во-вторых, каждая «датаграмма» (Linux) / «сообщение» (POSIX) несет флаг, называемый MSG_EOR.

Однако Linux SOCK_SEQPACKET для AF_UNIX не реализует MSG_EOR. Текущие документы не соответствуют действительности: -)


Предположительно, некоторые реализации SOCK_SEQPACKET делают другую. И некоторые реализуют оба. Так что охватывает все возможные разные комбинации: -)

[1] Пакетно-ориентированные протоколы обычно используют чтение на уровне пакетов с семантика усечения / отбрасывания и отсутствие MSG_EOR. X.25, Bluetooth, IRDA, и доменные сокеты Unix используют SOCK_SEQPACKET таким образом.

[2] Протоколы, ориентированные на записи, обычно используют чтение байтов и MSG_EOR - нет видимости на уровне пакетов, нет усечения / отбрасывания. DECNet и ISO TP используют SOCK_SEQPACKET таким образом.

[3] Гибриды пакетов / записей обычно используют SOCK_SEQPACKET с усечением / отбросить семантику на уровне пакета и записать завершающие пакеты отмечено MSG_EOR. SPX и XNS SPP используют SOCK_SEQPACKET таким образом.

https://mailarchive.ietf.org/arch/msg/tsvwg/9pDzBOG1KQDzQ2wAul5vnAjrRkA

Вы показали пример из пункта 1.

Пункт 2 также применяется к SOCK_SEQPACKET , как определено для SCTP . Хотя по умолчанию он устанавливает MSG_EOR на каждый sendmsg(). Возможность отключить это называется SCTP_EXPLICIT_EOR.

Пункт 3, наиболее соответствующий документам, представляется наиболее неясным.

И даже документы не соответствуют друг другу.

Тип сокета SOCK_SEQPACKET аналогичен типу SOCK_STREAM и также ориентирован на соединение. Разница между этими типами only заключается в том, что границы записей поддерживаются с использованием типа SOCK_SEQPACKET . Запись может быть отправлена ​​с использованием одной или нескольких операций вывода и получена с использованием одной или нескольких операций ввода, но одна операция никогда не передает части более чем одной записи. Границы записи видны получателю через флаг MSG_EOR в флагах полученных сообщений, возвращаемых функцией recvmsg (). - стандарт POSIX

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...