gpt4 book ai didi

c - SO_KEEPALIVE 在调用 write() 期间不起作用?

转载 作者:太空狗 更新时间:2023-10-29 15:30:22 25 4
gpt4 key购买 nike

我正在开发一个套接字应用程序,它必须对网络故障具有鲁棒性。

应用程序有 2 个正在运行的线程,一个等待来自套接字的消息(一个 read() 循环),另一个向套接字发送消息(一个 write() 循环)。

我目前正在尝试使用 SO_KEEPALIVE 来处理网络故障。如果我只在 read() 上被阻塞,它就可以正常工作。连接丢失几秒钟后(网络电缆被移除),read() 将失败并显示消息“连接超时”。

但是,如果我在网络断开后(超时结束前)尝试 wrte(),write() 和 read() 将永远阻塞,不会出错。

这是一个剥离的示例代码,它将 stdin/stdout 定向到套接字。它在端口 5656 上监听:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>

int socket_fd;

void error(const char *msg) {
perror(msg);
exit(1);
}

//Read from stdin and write to socket
void* write_daemon (void* _arg) {
while (1) {
char c;
int ret = scanf("%c", &c);
if (ret <= 0) error("read from stdin");
int ret2 = write(socket_fd, &c, sizeof(c));
if (ret2 <= 0) error("write to socket");
}
return NULL;
}

//Read from socket and write to stdout
void* read_daemon (void* _arg) {
while (1) {
char c;
int ret = read(socket_fd, &c, sizeof(c));
if (ret <= 0) error("read from socket");
int ret2 = printf("%c", c);
if (ret2 <= 0) error("write to stdout");
}
return NULL;
}


//Enable and configure KEEPALIVE - To detect network problems quickly
void config_socket() {
int enable_no_delay = 1;
int enable_keep_alive = 1;
int keepalive_idle =1; //Very short interval. Just for testing
int keepalive_count =1;
int keepalive_interval =1;
int result;

//=> http://tldp.org/HOWTO/html_single/TCP-Keepalive-HOWTO/#setsockopt
result = setsockopt(socket_fd, SOL_SOCKET, SO_KEEPALIVE, &enable_keep_alive, sizeof(int));
if (result < 0)
error("SO_KEEPALIVE");

result = setsockopt(socket_fd, SOL_TCP, TCP_KEEPIDLE, &keepalive_idle, sizeof(int));
if (result < 0)
error("TCP_KEEPIDLE");

result = setsockopt(socket_fd, SOL_TCP, TCP_KEEPINTVL, &keepalive_interval, sizeof(int));
if (result < 0)
error("TCP_KEEPINTVL");

result = setsockopt(socket_fd, SOL_TCP, TCP_KEEPCNT, &keepalive_count, sizeof(int));
if (result < 0)
error("TCP_KEEPCNT");
}

int main(int argc, char *argv[]) {
//Create Server socket, bound to port 5656
int listen_socket_fd;
int tr=1;
struct sockaddr_in serv_addr, cli_addr;
socklen_t clilen = sizeof(cli_addr);
pthread_t write_thread, read_thread;

listen_socket_fd = socket(AF_INET, SOCK_STREAM, 0);
if (listen_socket_fd < 0)
error("socket()");

if (setsockopt(listen_socket_fd,SOL_SOCKET,SO_REUSEADDR,&tr,sizeof(int)) < 0)
error("SO_REUSEADDR");

bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(5656);
if (bind(listen_socket_fd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
error("bind()");

//Wait for client socket
listen(listen_socket_fd,5);
socket_fd = accept(listen_socket_fd, (struct sockaddr *) &cli_addr, &clilen);
config_socket();
pthread_create(&write_thread, NULL, write_daemon, NULL);
pthread_create(&read_thread , NULL, read_daemon , NULL);
close(listen_socket_fd);
pthread_exit(NULL);
}

要重现错误,请使用 telnet 5656。If 将在连接丢失后的几 os 秒后退出,除非我尝试在终端中写一些东西。在这种情况下,它将永远阻塞。

所以,问题是:怎么了?如何解决?还有其他选择吗?

谢谢!


我试过使用 Wireshark 检查网络连接。如果我不调用 write(),我可以看到正在发送 TCP keep-alive 包并且连接在几秒后关闭。

相反,如果我尝试 write(),它会停止发送 Keep-Alive 数据包,并开始发送 TCP 重传(对我来说似乎没问题)。问题是,每次失败后,重传之间的时间间隔越来越大,似乎永远不会放弃并关闭套接字。

有没有办法设置最大重传次数或类似的东西?谢谢

最佳答案

我找到了 TCP_USER_TIMEOUT 套接字选项 (rfc5482),如果发送的数据在指定的时间间隔后未确认,它会关闭连接。

它对我来说很好 =)

//defined in include/uapi/linux/tcp.h (since Linux 2.6.37)
#define TCP_USER_TIMEOUT 18

int tcp_timeout =10000; //10 seconds before aborting a write()

result = setsockopt(socket_fd, SOL_TCP, TCP_USER_TIMEOUT, &tcp_timeout, sizeof(int));
if (result < 0)
error("TCP_USER_TIMEOUT");

然而,我觉得我不应该同时使用 SO_KEEP_ALIVE 和 TCP_USER_TIMEOUT。也许是某处的错误?

关于c - SO_KEEPALIVE 在调用 write() 期间不起作用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7768894/

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