tcp连接listen的backlog剖析

TCP连接中,最重要的是连接TCP连接上,两个方向之间的各个状态及各个系统调用与状态之间的关系。往往可以以两种图表示,第一种是状态转换图,第二种是连接时序图。如下:

状态图:

时序图:

可见,listen状态是服务器接收连接建立的必经之路。调用listen后,服务器即进入了LISTEN状态。

listen为:

int listen(int sockfd, int backlog);

其backlog是一个建议值,用于指定内部的队列大小,以控制同时建立的连接请求数量。

针对控制连接这个需求,有两种方法实现这个backlog:

  1. 单一队列来控制连接。队列中既包含了SYN_RCVD的状态,也包含了ESTABLISHED状态。accept只处理后面一种状态。如果三次握手中的ACK到来,则会在队列中直接改其状态。显然,这时backlog为这一队列的长度。
  2. 两个单独队列来控制。两种状态分别实现单独的队列。显然这种情况下,两个队列都必须有明确的大小限制,backlog只能限制其中一个。

在UNP中,这个值是这样描述的:

内核维护两个队列,一个是未完成队列,一个是已建立连接的队列。在第一个sync到达时,先将连接塞入第一个队列,再回复ACK+SYNC。此时,连接状态变为SYN_RCVD;

第二个是完成队列。客户端的ACK上来后,对应的连接移入完成队列。此状态的连接会被accept系统调用返回,状态变为ESTABLISHED。

另一方面,如果请求上来时队列已满,则TCP忽略之。客户端来重试。

可见,其实现实际是第一种单队列的形式,即backlog控制两种状态连接数的总和。

而对于linux:

man listen可见:

The behavior of the backlog argument on TCP sockets changed with Linux 2.2. Now it specifies the queue length forcompletely established sockets waiting to be accepted, instead of the number of incomplete connection requests. The maximum length of the queue for incomplete sockets can be set using /proc/sys/net/ipv4/tcp_max_syn_backlog.

可见,与UNP书中描述并不一致。是上述的第二种实现方法:两个队列,其中未完成队列长度限制以内核参数来表示,而已完成队列才是backlog。

cat /proc/sys/net/ipv4/tcp_max_syn_backlog

2048

具体代码:

TCP收到最终ACK后,重建一个sock:

tcp_v4_syn_recv_sock函数中:

if (sk_acceptq_is_full(sk))

goto exit_overflow;

newsk = tcp_create_openreq_child(sk, req, skb);

if (!newsk)

goto exit_nonewsk;

可见,在新生成一个socket时,先判断是否full。

static inline bool sk_acceptq_is_full(const struct sock *sk)

{

return sk->sk_ack_backlog > sk->sk_max_ack_backlog;

}

/*

*    Move a socket into listening state.

*/

int inet_listen(struct socket *sock, int backlog)中:

sk->sk_max_ack_backlog = backlog;

backlog传给了sk_max_ack_backlog。可见,此backlog用作判断complete queue。

在往外层,调用处:

child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb, req, NULL,

req, &own_req);

if (!child)

goto listen_overflow;

sock_rps_save_rxhash(child, skb);

tcp_synack_rtt_meas(child, req);

return inet_csk_complete_hashdance(sk, child, req, own_req);

listen_overflow:

if (!sysctl_tcp_abort_on_overflow) {

inet_rsk(req)->acked = 1;

return NULL;

}

embryonic_reset:

if (!(flg & TCP_FLAG_RST)) {

/* Received a bad SYN pkt - for TFO We try not to reset

* the local connection unless it‘s really necessary to

* avoid becoming vulnerable to outside attack aiming at

* resetting legit local connections.

*/

req->rsk_ops->send_reset(sk, skb);

} else if (fastopen) { /* received a valid RST pkt */

reqsk_fastopen_remove(sk, req, true);

tcp_reset(sk);

}

if (!fastopen) {

inet_csk_reqsk_queue_drop(sk, req);

NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_EMBRYONICRSTS);

}

return NULL;

可见,如果backlog满了,需要关注sysctl_tcp_abort_on_overflow,而些值是/proc下配置。

如果没有配,则啥都没干,直接忽略,这会使server端一定时间段重发SYNC/ACK。如果配置了,则走到下面发RST的流程。

另一点,客户端在发送完最后一个ACK,则进入了ESTABLISHED状态,但如上所述,这个ACK可能因为server端的backlog问题使得被忽略。但客户端并不知道,因为它是ESTABLISHED,它这段时间可以发数据!而后续重发来的SYNC/ACK,会使得客户端重发已经发出的数据,赞成带宽浪费。而TCP slow start会缓减这种情况。

backlog已满的情况下,不仅对于complete queue,对于ack queue也会有处理,适当限速(?)以丢掉一部分的sync,避免太拥堵。在函数中:

int tcp_conn_request(struct request_sock_ops *rsk_ops,

const struct tcp_request_sock_ops *af_ops,

struct sock *sk, struct sk_buff *skb)

。。。

/* 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) {

NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS);

goto drop;

}

总结:

  1. 服务端的连接建立有两个地方需要排队,一是sync包上来时,另一个是ACK上来时。对于linux来说,前者长度由配置项决定,机器上是2048;后者由listen带入的backlog指定;
  2. 如果backlog对应的队列满后,判断配置项,如果配了,rst,否则直接忽略,等待超时重发;
  3. 客户端只要发送了ACK后,即进入established状态,而服务端则要accept后。两者不对等,且客户端建立后立即可发送数据。
  4. sync队列也会对complete队列是否满进行参考。如果满了,限制收包速度,适当丢弃一些包。
时间: 2024-08-09 08:54:37

tcp连接listen的backlog剖析的相关文章

TCP连接突然断开的处理方法

TCP是因特网中的传输层协议,使用三次握手协议建立连接,下面是TCP建立连接的全过程. TCP断开连接的过程:TCP四次挥手. TCP/IP 协议簇分层结构 数据链路层主要负责处理传输媒介等众多的物理接口细节: 网络层负责处理数据分组在网络中的活动,包括上层数据报文的分割.选路 等: 传输层则负责为两台主机提供端到端的通信: 应用层将负责处理应用程序的特定细节. 其中,IP 协议是网络层的核心协议,用来提供不可靠.无连接的数据传递服务:而 TCP 协议则处于传输层,其基于不可靠无连接的 IP 协

libevent (二) 接收TCP连接

libevent 接收TCP连接 Evconnlistener 机制为您提供了侦听和接受传入的 TCP 连接的方法.下面的函数全部包含在`<event2/listener.h>`中. evconnlistener 创建监听对象 struct evconnlistener *evconnlistener_new(struct event_base *base,evconnlistener_cb cb, void *ptr, unsigned flags, int backlog,evutil_s

《TCP/IP具体解释》读书笔记(18章)-TCP连接的建立与中止

TCP是一个面向连接的协议.不管哪一方向还有一方发送数据之前.都必须在两方之间建立一条连接.这样的两端间连接的建立与无连接协议UDP不同.UDP向还有一端发送数据报时,无需不论什么预告的握手. 1.建立连接的协议(3次握手) 1)请求端发送一个SYN段指明client打算连接的serverport,以及初始序列号. 2)server发回包含server的初始序号的SYN报文段作为应答.同一时候将确认序号设置为客户的ISN加1以对客户的SYN报文段进行确认.一个SYN将占用一个序号. 3)clie

《TCP/IP详解》读书笔记(18章)-TCP连接的建立与中止

TCP是一个面向连接的协议.无论哪一方向另一方发送数据之前,都必须在双方之间建立一条连接.这种两端间连接的建立与无连接协议UDP不同,UDP向另一端发送数据报时,无需任何预告的握手. 1.建立连接的协议(3次握手) 1)请求端发送一个SYN段指明客户端打算连接的服务器端口,以及初始序列号. 2)服务器发回包含服务器的初始序号的SYN报文段作为应答.同时将确认序号设置为客户的ISN加1以对客户的SYN报文段进行确认.一个SYN将占用一个序号. 3)客户端将确认序号设置为服务器的ISN加1以对服务器

TCP连接的状态详解以及故障排查

转载自CSDN博客:http://blog.csdn.net/hguisu/article/details/38700899 TCP状态 TCP状态迁移路线图 TCP连接建立三次握手 TCP连接的终止四次握手释放 同时打开 同时关闭 TCP通信中服务器处理客户端意外断开 Linux错误信息errno列表 我们通过了解TCP各个状态,可以排除和定位网络或系统故障时大有帮助.(总结网络上的内容) 1.TCP状态 了解TCP之前,先了解几个命令:   linux查看tcp的状态命令: 1).netst

服务器后台TCP连接存活问题

0. 背景 公司的服务器后台部署在某一个地方,接入的是用户的APP,而该地方的网络信号较差,导致了服务器后台在运行一段时间后用户无法接入,那边的同事反馈使用netstat查看系统,存在较多的TCP连接. 1. 问题分析 首先在公司内部测试服务器上部署,使用LoadRunner做压力测试,能正常运行,然后那边的同事反馈该地方信号较差.考虑到接入的问题,有可能接入进程的FD资源耗尽,导致accept失败.推论的依据是对于TCP连接来说,如果客户端那边由于一些异常情况导致断网而未能向服务器发起FIN关

socket连接和TCP连接的关系

我们在传输数据时,可以只使用(传输层)TCP/IP协议,但是那样的话,如果没有应用层,便无法识别数据内容,如果想要使传输的数据有意义,则必须使用到应用层协议,应用层协议有很多,比如HTTP.FTP.TELNET等,也可以自己定义应用层协议.WEB使用HTTP协议作应用层协议,以封装HTTP文本信息,然后使用TCP/IP做传输层协议将它发到网络上. 1)Socket是一个针对TCP和UDP编程的接口,你可以借助它建立TCP连接等等.而TCP和UDP协议属于传输层 . 而http是个应用层的协议,它

TCP连接解释及连接过程描述

1.TCP连接的建立 设主机B运行一个服务器进程,它先发出一个被动打开命令,告诉它的TCP要准备接收客户进程的连续请求,然后服务进程就处于听的状态.不断检测是否有客户进程发起连续请求,如有,作出响应.设客户进程运行在主机A中,他先向自己的TCP发出主动打开的命令,表明要向某个IP地址的某个端口建立运输连接,过程如下: 1)主机A的TCP向主机B的TCP发出连接请求报文段,其首部中的同步比特SYN应置1,同时选择一个序号x,表明在后面传送数据时的第一个数据字节的序号是x. 2)主机B的TCP收到连

转载:TCP连接的状态详解以及故障排查

FROM:http://blog.csdn.net/hguisu/article/details/38700899 该博文的条理清晰,步骤明确,故复制到这个博文中收藏,若文章作者看到且觉得不能装载,麻烦请告知,谢谢. 我们通过了解TCP各个状态,可以排除和定位网络或系统故障时大有帮助.(总结网络上的内容) 1.TCP状态 linux查看tcp的状态命令: 1).netstat -nat  查看TCP各个状态的数量 2).lsof  -i:port  可以检测到打开套接字的状况 3).  sar