gpt4 book ai didi

c - 正确处理 TCP 带外数据

转载 作者:行者123 更新时间:2023-12-05 07:55:18 31 4
gpt4 key购买 nike

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

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

我的最后一个问题是,由于客户端立即发送带外数据,服务器是否有机会在三向握手完成后但从接受返回之前收到 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
>

最佳答案

听说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 字节变成正常数据(作为正常数据插入流中),即使 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 中编码 ~,这将使 anchor 无效

//那些手册页直到今天仍然不支持 anchor ,很遗憾。

关于c - 正确处理 TCP 带外数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29996879/

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com