Не удается прочитать RTP-пакет (или получить его размер) из многоадресного сокета с помощью нескольких вызовов read - PullRequest
0 голосов
/ 21 января 2020

У меня проблемы с чтением RTP-пакетов из многоадресного сокета, который открывается с помощью следующей функции:

int
    open_multicast_socket
        (const char *group_address,
         uint16_t port)
{
    assert(group_address != NULL);

    int
        s;

    if (-1 != (s = socket(
        AF_INET, SOCK_DGRAM, 0
    )))
    {
        int
            reuse = 1;

        if (-1 != setsockopt(
            s, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof reuse
        ))
        {
            struct sockaddr_in
                sock_addr;

            bzero(&sock_addr, sizeof sock_addr);

            if (1 == inet_pton(
                AF_INET, group_address, &sock_addr.sin_addr
            ))
            {
                sock_addr.sin_family      = AF_INET;
                sock_addr.sin_port        = htons(port);

                if (0 == bind(
                    s, (struct sockaddr*)&sock_addr, sizeof sock_addr
                ))
                {
                    struct ip_mreq
                        mreq = {
                            .imr_multiaddr.s_addr = inet_addr(group_address),
                            .imr_interface.s_addr = htonl(INADDR_ANY)
                        };

                    if (0 == setsockopt(
                        s, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof mreq
                    ))
                    {
                        //fcntl(s, F_SETFL, O_NONBLOCK);
                        return s;
                    } // setsockopt
                    else
                    {
                        perror("setsockopt");
                        close(s);
                    }
                } // bind
                else
                {
                    perror("bind");
                    close(s);
                }
            } // inet_pton
            else
            {
                perror("inet_pton");
                close(s);
            }
        } // setsockopt
        else
        {
            perror("setsockopt");
            close(s);
        }
    } // socket
    else
    {
        perror("socket");
    }

    return -1;
}

Если я читаю заголовок RTP плюс полезную нагрузку в одной операции read, я получаю весь пакет. Однако, если я сначала пытаюсь получить заголовок RTP, а затем - пользовательский заголовок в полезной нагрузке - 2-й read всегда получает вместо этого следующий заголовок RTP, отбрасывая все присоединенные данные. Поскольку длина полезной нагрузки может варьироваться, кажется, что единственный способ получить целый пакет - угадать его максимально возможный размер.

Я пытался получить количество доступных байтов перед чтением:

ioctl(sock, FIONREAD, &nbytes);

но всегда возвращает 0.

Опрос в сокете всегда завершается неудачей, как будто данные вообще не доступны.

Когда включена неблокирующая функция (т. Е. fcntl(sock, F_SETFL, O_NONBLOCK);) - read всегда терпит неудачу (-1), так же, как и recv(sock, buf, buf_len, MSG_DONTWAIT).

Так есть ли способ правильно проанализировать пакеты RTP с помощью последовательных неблокирующих вызовов read?

Non- блокировка необходима, потому что должна быть возможность проверить, было ли потеряно соединение, и при необходимости заново открыть сокет.

Ответы [ 2 ]

1 голос
/ 27 января 2020

В отличие от TCP, который является потоковым протоколом, UDP является пакетным протоколом. Это означает, что всякий раз, когда вы читаете из сокета UDP (многоадресная или нет), вы получите ровно одну дейтаграмму UDP. Если ваш буфер недостаточно велик, чтобы вместить всю дейтаграмму, оставшиеся данные по существу теряются.

Убедитесь, что ваш буфер достаточно большой, чтобы вместить полную дейтаграмму. Если ваша сеть поддерживает сквозные кадры большого размера, это означает, что ваш буфер должен быть 9000 байт, в противном случае он должен быть 1500 байт.

0 голосов
/ 22 января 2020

Нужно прочитать весь буфер из сокета и затем проанализировать их.

Можно создать буфер размера MTU, прочитать из сокета в этот временный буфер, а затем проанализировать весь буфер и затем выполнить действие.

Можно использовать select () или poll () проверить, есть ли данные в сокете. Прочтите его, когда он станет доступен.

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