您的位置:首页 > 其它

Listen的backlog参数现象解析

2016-04-29 18:42 615 查看
1. 背景:

        面试偶尔会被问到backlog作用,之前只知道是监听队列的长度,本文先通过抓包了解listen的backlog。

2. 官方文档的解释:

The behavior of the backlog argument on TCP sockets changed with Linux 2.2.  Now it specifies
the queue length for completely established sockets waiting to be accepted,  instead  of  the
number  of  incomplete  connection  requests.
       The backlog argument defines the maximum length to which the queue of pending connections for
       sockfd may grow.  If a connection request arrives when the queue  is  full,  the  client  may
       receive  an  error with an indication of ECONNREFUSED or, if the underlying protocol supports
       retransmission, the request may be ignored so that a later reattempt at connection  succeeds.

        man listen出来的结果中显示,backlog在内核2.2版本之后,表示的是建立连接的队列长度,而不是之前版本中的不完全的连接(SYNC_RECV状态)。

        如果队列满,则会尝试重传。

3. 现象:

        服务端代码:服务端监听后不做任何接受操作。

#define CHECK(s) do { \
if ((s) == -1) { \
printf ("Internal error in %d\n", __LINE__); \
printf ("ERROR : %s\n", strerror (errno)); \
exit (1); \
} \
} while (0)

#define PORT 8888

int main ()
{
int fd;
struct sockaddr_in saddr;

fd = socket (AF_INET, SOCK_STREAM, 0);
CHECK (fd);

memset (&saddr, 0, sizeof (saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons (PORT);
saddr.sin_addr.s_addr = htonl (INADDR_ANY);
CHECK (bind (fd, (struct sockaddr *)&saddr, sizeof (saddr)));

CHECK (listen (fd, 1));

while (1) {
}

return 0;
}
        客户端代码:客户端连接上以后,发送一次数据,只是为了让抓包结果更加直观。
#define PORT 8888
#define CHECK(s) do { \
if ((s) == -1) { \
printf ("Internal error in %d\n", __LINE__); \
printf ("%s\n", strerror (errno)); \
exit (0); \
} \
} while (0)

int main ()
{
int fd1;
char c;
struct sockaddr_in saddr;

fd1 = socket (AF_INET, SOCK_STREAM, 0);
CHECK (fd1);
memset (&saddr, 0, sizeof (saddr));

saddr.sin_family = AF_INET;
saddr.sin_port = htons (PORT);
inet_pton (AF_INET, "127.0.0.1", (struct sockaddr *)&saddr.sin_addr);

CHECK (connect (fd1, (struct sockaddr *)&saddr, sizeof (saddr)));
printf ("Connected.\n");
CHECK (write (fd1, "1", 1));
while (1) {}
close (fd1);
return 0;
}

        服务端监听backlog1,同时起三个客户端,抓包分析:

18:18:57.440033 IP localhost.57454 > localhost.ddi-tcp-1: Flags [S], seq 3679023284, win 32792, options [mss 16396,sackOK,TS val 616561358 ecr 0,nop,wscale 6], length 0

18:18:57.440048 IP localhost.ddi-tcp-1 > localhost.57454: Flags [S.], seq 3179121074, ack 3679023285, win 32768, options [mss 16396,sackOK,TS val 616561358 ecr 616561358,nop,wscale 6], length 0

18:18:57.440058 IP localhost.57454 > localhost.ddi-tcp-1: Flags [.], ack 1, win 513, options [nop,nop,TS val 616561358 ecr 616561358], length 0
18:18:57.440808 IP localhost.57455 > localhost.ddi-tcp-1: Flags [S], seq 2061186705, win 32792, options [mss 16396,sackOK,TS val 616561359 ecr 0,nop,wscale 6], length 0

18:18:57.440816 IP localhost.ddi-tcp-1 > localhost.57455: Flags [S.], seq 3149774749, ack 2061186706, win 32768, options [mss 16396,sackOK,TS val 616561359 ecr 616561359,nop,wscale 6], length 0

18:18:57.440823 IP localhost.57455 > localhost.ddi-tcp-1: Flags [.], ack 1, win 513, options [nop,nop,TS val 616561359 ecr 616561359], length 0
18:18:57.441156 IP localhost.57456 > localhost.ddi-tcp-1: Flags [S], seq 1136648881, win 32792, options [mss 16396,sackOK,TS val 616561359 ecr 0,nop,wscale 6], length 0

18:18:57.441163 IP localhost.ddi-tcp-1 > localhost.57456: Flags [S.], seq 1168134482, ack 1136648882, win 32768, options [mss 16396,sackOK,TS val 616561359 ecr 616561359,nop,wscale 6], length 0

18:18:57.441169 IP localhost.57456 > localhost.ddi-tcp-1: Flags [.], ack 1, win 513, options [nop,nop,TS val 616561359 ecr 616561359], length 0

18:18:57.441227 IP localhost.57456 > localhost.ddi-tcp-1: Flags [P.], seq 1:2, ack 1, win 513, options [nop,nop,TS val 616561359 ecr 616561359], length 1

18:18:57.443529 IP localhost.57455 > localhost.ddi-tcp-1: Flags [P.], seq 1:2, ack 1, win 513, options [nop,nop,TS val 616561362 ecr 616561359], length 1

18:18:57.443536 IP localhost.ddi-tcp-1 > localhost.57455: Flags [.], ack 2, win 512, options [nop,nop,TS val 616561362 ecr 616561362], length 0
18:18:57.446530 IP localhost.57454 > localhost.ddi-tcp-1: Flags [P.], seq 1:2, ack 1, win 513, options [nop,nop,TS val 616561365 ecr 616561358], length 1

18:18:57.446538 IP localhost.ddi-tcp-1 > localhost.57454: Flags [.], ack 2, win 512, options [nop,nop,TS val 616561365 ecr 616561365], length 0
18:18:57.641511 IP localhost.57456 > localhost.ddi-tcp-1: Flags [P.], seq 1:2, ack 1, win 513, options [nop,nop,TS val 616561560 ecr 616561359], length 1

18:18:58.043509 IP localhost.57456 > localhost.ddi-tcp-1: Flags [P.], seq 1:2, ack 1, win 513, options [nop,nop,TS val 616561962 ecr 616561359], length 1

18:18:58.640516 IP localhost.ddi-tcp-1 > localhost.57456: Flags [S.], seq 1168134482, ack 1136648882, win 32768, options [mss 16396,sackOK,TS val 616562559 ecr 616561962,nop,wscale 6], length 0

18:18:58.640543 IP localhost.57456 > localhost.ddi-tcp-1: Flags [.], ack 1, win 513, options [nop,nop,TS val 616562559 ecr 616561359], length 0

18:18:58.847508 IP localhost.57456 > localhost.ddi-tcp-1: Flags [P.], seq 1:2, ack 1, win 513, options [nop,nop,TS val 616562766 ecr 616561359], length 1

18:19:00.455514 IP localhost.57456 > localhost.ddi-tcp-1: Flags [P.], seq 1:2, ack 1, win 513, options [nop,nop,TS val 616564374 ecr 616561359], length 1

18:19:00.640517 IP localhost.ddi-tcp-1 > localhost.57456: Flags [S.], seq 1168134482, ack 1136648882, win 32768, options [mss 16396,sackOK,TS val 616564559 ecr 616564374,nop,wscale 6], length 0

18:19:00.640548 IP localhost.57456 > localhost.ddi-tcp-1: Flags [.], ack 1, win 513, options [nop,nop,TS val 616564559 ecr 616561359], length 0

18:19:03.671514 IP localhost.57456 > localhost.ddi-tcp-1: Flags [P.], seq 1:2, ack 1, win 513, options [nop,nop,TS val 616567590 ecr 616561359], length 1

18:19:10.103515 IP localhost.57456 > localhost.ddi-tcp-1: Flags [P.], seq 1:2, ack 1, win 513, options [nop,nop,TS val 616574022 ecr 616561359], length 1

18:19:10.103545 IP localhost.ddi-tcp-1 > localhost.57456: Flags [R], seq 1168134483, win 0, length 0


        三条连接分别对应三个端口,57454(红色),57455(蓝色),57456,从抓包上来看57454和57455两个端口的数据很快就已经发送到服务端,说明他们连接成功建立了,在50ms之内服务端已经连数据一起确认完了。

        剩下黑色部分的57456端口的抓包信息看:

        第一次三次握手之后,客户端在发送完syn的ack之后,开始发送数据,间隔一次为1s(57-58), 2s (58-00),3s (00-03), 6.5s (03-10),但是数据却一直没有得到确认。

        反观服务端发送信息,在57s发送一个syn + ack之后,收到的ack应该因为队列已满,直接丢弃了。

        第二次在58s发送syn + ack,由于队列没有被accept,因此客户端回复的ack以及数据一起携带的ack全部都被丢弃。

        第三次在00s发送syn + ack,同样丢弃了所有ack和数据,时间间隔分别为1s, 2s (注:由于本机上net.ipv4.tcp_synack_retries = 2,只重试两次而非普通的1,2,4,8,16,32)。

        最后一次发送的超时应该为4s,应该在04之后,因此03之前的数据都被丢弃,但是没有发送RST重置连接。

        但是在04秒之后的下一个数据包在10s到来,这时候已经达到了重试的最大次数,因此就向对端发送了RST信号。

4. 总结:

        listen的backlog控制的是三次握手成功的队列,当队列已满时候,服务端会丢弃ack确认报文,按照ack丢包的方式进行重传处理。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: