Правильная обработка внеполосных данных TCP

Я написал простой клиент и сервер для работы с внеполосными данными. Клиент просто отправляет на сервер одиночные внеполосные данные, и сервер использует SIGURG для обработки этого единственного байта. Сервер также должен обрабатывать нормальный трафик в бесконечном цикле. Код имеет состояние гонки, которое не работает, как ожидалось. Иногда я получаю "Неверный аргумент" от вызова recv() в обработчике SIGURG. Другой вопрос, который у меня возникает, заключается в том, должен ли я блокировать сигнал SIGURG при вызове accept? Кроме того, какой из них является предпочтительным сценарием:

  • установите обработчик SIGURG и установите владельца сокета для прослушивающего сокета перед вызовом accept.
  • установите обработчик SIGURG и установите владельца сокета для подключенного сокета после вызова accept.
  • Если ничего из вышеперечисленного, пожалуйста, напишите ваше предложение.

Мой последний вопрос: поскольку клиент немедленно отправляет внеполосные данные, есть ли у сервера возможность получить SIGURG сразу после завершения трехстороннего рукопожатия, но до возвращения из принятия? Если это так, я думаю, что переменная "clifd" может иметь недопустимое значение, когда она используется в обработчике SIGURG.

код для клиента:

#include "myheader.h"

int main(int argc, char *argv[])
{
    struct sockaddr_in saddr;
    int sockfd;

    const char c = 'a';

    if (2 != argc)
    {
        fprintf(stderr, "Usage: %s ipaddr\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    if (-1 == (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
        die("sockfd()");

    (void)memset(&saddr, 0, sizeof(saddr));

    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(PORT);

    if (-1 == inet_pton(AF_INET, argv[1], &saddr.sin_addr))
        die("inet_pton()");

    if (-1 == connect(sockfd, (struct sockaddr*)&saddr, sizeof(saddr)))
        die("connect()");

//  if (-1 == send(sockfd, "HELLO\n", 6, 0))
//      die("send()");

    if (-1 == send(sockfd, &c, 1, MSG_OOB))
        die("send()");

    close(sockfd);

    return 0;
}

и код для сервера:

#include "myheader.h"

void sigurg_handler(int);

char    oob;
int sockfd, clifd;

int main(void)
{
    struct sockaddr_in myaddr;
    char buf[BUFSIZ];
    ssize_t nbytes;
    sigset_t sset, oset;
    sigemptyset(&sset);
    sigaddset(&sset, SIGURG);

    if (-1 == (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
        die("socket()");

    (void)memset(&myaddr, 0, sizeof(myaddr));

    myaddr.sin_family = AF_INET;
    myaddr.sin_port = htons(PORT);
    myaddr.sin_addr.s_addr = htonl(INADDR_ANY);

    if (-1 == bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))
        die("bind()");

    if (-1 == listen(sockfd, BACKLOG))
        die("listen()");

    if (-1 == fcntl(sockfd, F_SETOWN, getpid()))
        die("fcntl()");

    if (SIG_ERR == signal(SIGURG, sigurg_handler))
            die("signal()");
    for (;;)
    {
        /* block SIGURG when accepting the connection */
//      sigprocmask(SIG_SETMASK, &sset, &oset);
        printf("bloking in accept()\n");
        if (-1 == (clifd = accept(sockfd, NULL, NULL)))
            die("accept()");

        /* unblock SIGURG */
//      sigprocmask(SIG_SETMASK, &oset, NULL);

        printf("recv()ing normal data\n");
        nbytes = recv(clifd, buf, sizeof(buf), 0);
        buf[nbytes] = 0; /* null-terminate */

        printf("%s", buf);

    }

    close(sockfd);
}

void
sigurg_handler(int signo)
{
    char buff[100];
    ssize_t nbytes;

    printf("SIGURG received\n");
    if (clifd != 0)
    {
        if (-1 == (nbytes = recv(clifd, buff, sizeof(buff) - 1, MSG_OOB)))
            die("recv() in sigurg_handler()");

        buff[nbytes] = 0;
        printf("from sigurg_handler: received \"%s\"\n", buff);
    }
    else
    {
        printf("clifd = %d\n", clifd);
        exit(1);
    }
}

Пример:

> ./serv 
bloking in accept()         /* first client */
SIGURG received
from sigurg_handler: received "a"
recv()ing normal data
bloking in accept()         /* second client */
SIGURG received
recv() in sigurg_handler(): Invalid argument
> ./serv             /* third client */
bloking in accept()
SIGURG received
clifd = 0
>

1 ответ

Я слышалselect()третий параметр может обрабатывать tcp OOB

      ret = select(connfd+1,&read_fds,NULL,&exceptions_fds,NULL);

https://blog.csdn.net/ty_laurel/article/details/52164669
https://github.com/ty92/OOB



select() исключительный

использоватьselectдействительно можно избежать шага настройки сигнала,
так что вы не пропустите oob (то, что до настройки сигнала).

https://man7.org/linux/man-pages/man7/tcp.7.html#:~:text=out%2Dof%2Dband%20data%20is%20present

man 2 select_tutимеет демонстрационный код
https://man7.org/linux/man-pages/man2/select_tut.2.html#:~:text=read%20OOB%20data,-before


ограничение

но если вы не прочитали байт oob вовремя, когда прибудет новый байт oob, старый байт oob станет обычными данными (inserted as normal data into the stream), даже еслиSO_OOBINLINEне установлено (в linux)
// это поведение может отличаться в разных стеках tcp.

https://man7.org/linux/man-pages/man7/tcp.7.html#:~:text=limited%20support%20for%20out%2Dof%2Dband



PS: ссылки лучше копировать с:~:text=вручную он выделит ключевое слово в chrome.
// или щелкните в режиме предварительного просмотра редактирования.
// на обычной странице stackoverflow всегда кодирует~в URL, что аннулирует привязку

// эти справочные страницы до сих пор не поддерживают якоря, жаль.

Другие вопросы по тегам