从linux源码看socket(tcp)的timeout

前言

网络编程中超时时间是一个重要但又容易被忽略的问题,对其的设置需要仔细斟酌。在经历了数次物理机宕机之后,笔者详细的考察了在网络编程(tcp)中的各种超时设置,于是就有了本篇博文。本文大部分讨论的是socket设置为block的情况,即setNonblock(false),仅在最后提及了nonblock socket(本文基于linux 2.6.32-431内核)。

connectTimeout

在讨论connectTimeout之前,让我们先看下java和C语言对于socket connect调用的函数签名:

java:
 // 函数调用中携带有超时时间
 public void connect(SocketAddress endpoint, int timeout) ;
C语言:
 // 函数调用中并不携带超时时间
 int connect(int sockfd, const struct sockaddr * sockaddr, socklen_t socklent) 	 

操作系统提供的connect系统调用并没有提供timeout的参数设置而java却有,我们先考察一下原生系统调用的超时策略。

connect系统调用

我们观察一下此系统调用的kernel源码,调用栈如下所示:

connect[用户态]
	|->SYSCALL_DEFINE3(connect)[内核态]
			|->sock->ops->connect

由于我们考察的是tcp的connect,其socket的内部结构如下图所示: 
最终调用的是tcp_connect,代码如下所示:

int tcp_connect(struct sock *sk) {
	......
	// 发送SYN
	err = tcp_transmit_skb(sk, buff, 1, sk->sk_allocation);
	...
	/* Timer for repeating the SYN until an answer. */
	// 由于是刚建立连接,所以其rto是TCP_TIMEOUT_INIT
	inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS,
				inet_csk(sk)->icsk_rto, TCP_RTO_MAX);
	return 0;	
}

又上面代码可知,在tcp_connect设置了重传定时器之后return回了tcp_v4_connect再return到inet_stream_connect。我们继续考察:

int inet_stream_connect(struct socket *sock, struct sockaddr *uaddr,
			int addr_len, int flags)
{
	......
	// tcp_v4_connect=>tcp_connect
	err = sk->sk_prot->connect(sk, uaddr, addr_len);
	// 这边用的是sk->sk_sndtimeo
	timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
	......
	inet_wait_for_connect(sk, timeo));
	......
out:
	release_sock(sk);
	return err;

sock_error:
	err = sock_error(sk) ? : -ECONNABORTED;
	sock->state = SS_UNCONNECTED;
	if (sk->sk_prot->disconnect(sk, flags))
		sock->state = SS_DISCONNECTING;
	goto out
}

由上面代码可见,可以采用设置SO_SNDTIMEO来控制connect系统调用的超时,如下所示:

setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &timeout, len);

不设置SO_SNDTIMEO

如果不设置SO_SNDTIMEO,那么会由tcp重传定时器在重传超过设置的时候后超时,如下图所示:


这个syn重传的次数由:

cat /proc/sys/net/ipv4/tcp_syn_retries 笔者机器上是5 

来决定。那么我们就来看一下这个重传到底是多长时间:

tcp_connect中:
		// 设置的初始超时时间为icsk_rto=TCP_TIMEOUT_INIT为1s
		inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS,
				inet_csk(sk)->icsk_rto, TCP_RTO_MAX);

其重传定时器的回掉函数为tcp_retransmit_timer:

void tcp_retransmit_timer(struct sock *sk)
{
	......
	// 检测是否超时
	if (tcp_write_timeout(sk))
		goto out;
	......
	// icsk_rto = icsk_rto * 2,由于syn阶段,所以isck_rto不会由于网络传输而改变
	// 重传的时候会以1,2,4,8指数递增
	icsk->
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值