Правильная обработка внеполосных данных 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, что аннулирует привязку
// эти справочные страницы до сих пор не поддерживают якоря, жаль.