之前对于通信两端如果发生长时间无数据通信的情况,可以通过设置 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;
}