Linux中listen()系统调用的backlog参数分析(3)

在启动测试程序之前,在客户端使用tcpdump抓包,并将输出结果通过-w选项存储在192.cap文件中,便于后续使用wireshark来分析。

测试发现,在客户端建立300个连接后,客户端建立连接的速度明显慢了很多,而且最终建立完1000个连接花了20分钟左右。使用wireshark打开192.cap文件,来看抓包的情况,发现在300个连接之后有大量的ack包重传,如下图所示:

Linux中listen()系统调用的backlog参数分析

在wireshark的过滤器中选择本地端口为49274的连接来具体分析,该连接抓包情况如下所示:

Linux中listen()系统调用的backlog参数分析

上面的图中可以看到,SYN包重传了一次;在正常的三次握手之后,服务器又发送了SYN+ACK包给客户端,导致客户段再次发送ACK,而且这个过程重复了5次。在wireshark中过滤其他连接,发现情况也是如此。

问题来了,为什么要重传SYN包?为什么在三次握手之后,服务器端还要重复发送SYN+ACK包?为什么重复了5次之后就不再发了呢?要解答这些问题,我们需要深入到内核代码中看三次握手过程中内核是如何处理的,以及在连接队列满之后是怎么处理。内核中处理客户端发送的SYN包是在tcp_v4_conn_request()函数中,关键代码如下所示:

int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
{
 ......

if (inet_csk_reqsk_queue_is_full(sk) && !isn) {
#ifdef CONFIG_SYN_COOKIES
  if (sysctl_tcp_syncookies) {
   want_cookie = 1;
  } else
#endif
  goto drop;
 }

/* Accept backlog is full. If we have already queued enough
  * of warm entries in syn queue, drop request. It is better than
  * clogging syn queue with openreqs with exponentially increasing
  * timeout.
  */
 if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1)
  goto drop;

req = inet_reqsk_alloc(&tcp_request_sock_ops);
 if (!req)
  goto drop;                 ......                   
 if (__tcp_v4_send_synack(sk, req, dst) || want_cookie)
  goto drop_and_free;

inet_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT);
 return 0;

drop_and_release:
 dst_release(dst);
drop_and_free:
 reqsk_free(req);
drop:
 return 0;
}

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/30c2be731595075c8993b6af16dcaabb.html