网络编程-TIMEOUT属性设置

Posted by 周思进 on March 8, 2021

之前对于通信两端如果发生长时间无数据通信的情况,可以通过设置 keepalive 机制来识别网络是否异常,如果网络异常,则主动关闭连接,可以看网络编程-keepalive一文。

不过在网络编程-断网重传中提到,如果是一方发送数据,但收不到对方应答,则断网重传一般过程就需要消耗15分钟的时间。而在明确收不到对方应答的情况下,你可能也不希望等如此之久,比如你可能希望20s没收到对方应答就关闭连接,该场景可以通过设置套接字 TIMEOUT 属性来解决。

下面示例代码通过 iptables 来模拟通信过程中,对端网络异常,本地收不到对端应答的情况,具体代码如下

int is_connect_closed(int sockfd)
{
	struct tcp_info info;
	int len = sizeof(info);
	int ret = -1;

	if (sockfd <= 0)
	{
		return 1;
	}
	
	memset(&info, 0, sizeof(info));
	ret = getsockopt(sockfd, IPPROTO_TCP, TCP_INFO, &info, (socklen_t *)&len);
	if (ret != 0)
	{
		return 1;
	}

	if ((info.tcpi_state == TCP_ESTABLISHED))
	{
		return 0;
	}
	else
	{
		return 1;
	}
}

int main(int argc, char *argv[])
{
    int fd = -1;
    int ret = -1;
    char buf[100] = {0};
    struct sockaddr_in  addr;
    int timeout = 20000;    // 20S
    int count = 1;

    fd = socket(AF_INET, SOCK_STREAM, 0);
    
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(atoi(argv[2]));
    inet_pton(AF_INET, argv[1], &addr.sin_addr);

    connect(fd, (struct sockaddr *)&addr, sizeof(addr));

    setsockopt(fd, SOL_TCP, TCP_USER_TIMEOUT, &timeout, sizeof(timeout));

    ret = is_connect_closed(fd);
    printf("ret:%d\n", ret);

    // 使用iptables 丢弃服务器方发送过来的数据
    system("iptables -A INPUT --src 192.168.2.32 -j DROP");

    sleep(2);
    ret = is_connect_closed(fd);
    printf("ret:%d\n", ret); // 连接仍旧是建立状态

    ret = write(fd, buf, 2);
    printf("ret:%d\n", ret); // 立即返回,实际数据一直在内核发送队列,可以发送出去,但收不到应答

    // 看大概需要多久连接会断开,如果未设置TCP_USER_TIMEOUT,则大概15分钟,设置TCP_USER_TIMEOUT,则以设置的时间后断开连接
    while (!is_connect_closed(fd))
    {
        sleep(1);
        printf("count:%d\n", count++);
    }

    return 0;
}