c-正确处理TCP带外数据



我编写了一个简单的客户端和服务器来处理带外数据。客户端只向服务器发送一个带外数据,服务器使用SIGURG来处理这个单字节。服务器还应该在无限循环中处理正常流量。该代码有一个争用条件,无法按预期工作。有时我会从SIGURG处理程序中对recv()的调用中得到一个"无效参数"。我的另一个问题是,当调用accept时,我应该屏蔽SIGURG信号吗?此外,哪一种是首选场景:

  • 在调用accept之前,安装SIGURG处理程序并设置侦听套接字的套接字所有者
  • 安装SIGURG处理程序,并在调用accept后为连接的套接字设置套接字所有者
  • 如果以上都没有,请写下你的建议

我的最后一个问题是,由于客户端立即发送带外数据,服务器是否有机会在三方握手完成后,但在从accept返回之前接收SIGURG?如果是这样的话,我认为"clifd"var在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 ipaddrn", 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, "HELLOn", 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 datan");
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 receivedn");
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 = %dn", 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
>

我听说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%2Band%20data%20is%20present

man 2 select_tut有一个演示代码
https://man7.org/linux/man-pages/man2/select_tut.2.html#:~:text=读取%2OOB%20数据,-在之前


限制

但是如果你没有及时读取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=限制%20support%20for%20out%2Dof%2Band



PS:你最好手动复制:~:text=的链接,它会在chrome中突出显示关键字
//或在编辑预览模式下单击
//在正常页面中,stackerflow总是在url中编码~,这将使锚无效

//那些手册页直到今天仍然不支持主播,真是遗憾。

最新更新