1. 概述
对于TCP服务器,使用bind绑定一个地址和端口,然后使用listen(backlog)在这个socket上监听用户的连接请求。其中,backlog参数的大小,决定了TCP服务器的连接请求队列大小,进一步影响客户端连接请求的成功与否。
本文档介绍通过实验的方式获得不同backlog参数值的不同影响。实验使用Python语言。
2 socket连接建立过程
2.1 服务器监听端口
通常需要以下三个步骤:
sock = socket.socket() #创建socket对象:
sock.bind((“”, 7890)) #将socket绑定到指定地址和端口上
sock.listen(5) #使socket监听指定地址和端口
然后,调用accept方法获得一个客户端连接请求。
client_sock, addr = sock.accept()
2.2 客户端建立连接
通常执行以下两个步骤:
sock = socket.socket() #创建socket对象
sock.connect((“”, 7890)) #请求连接指定地址和端口
2.3 服务器的连接请求队列
系统为正等待连接请求的TCP服务程序,维护一个固定长度的连接队列。这个队列中的连接是已经被TCP接收(已经完成三次握手),但是还没有被应用程序接收(调用accept函数)
队列的长度就是服务器调用listen时指定的。但指定的数值并不直接就是队列的长度,通常是需要经过公式转换的。
如下图所示,当客户连接请求到达时,系统底层处理这个请求,TCP握手成功完成后,将其放入连接队列,等待服务程序accept。
3 实验
3.1 实验步骤
首先,服务器端执行以下步骤:
1.
绑定地址和端口,并监听,等待客户端请求
2.
获取一个连接请求,进入睡眠10秒,
3.
接收客户端数据
4.
重复步骤2,3。
其次,客户端执行以下步骤:
1.
创建socket,请求连接服务器。
2.
等待连接成功,发送数据。
3.
关闭连接。
最后,同时启动多个客户端。由于服务器接收一个请求后,进入睡眠,当客户端数量达到一定数值时,将超过连接队列的长度。观察在这种情况下,不同客户端的连接状态,以及对应的行为。
3.2 实验代码
Server.py代码如下:
1 import socket
2 import time
3
4 def main():
5 sock = socket.socket()
6 host = ""
7 port = 7890
8 sock.bind((host, port))
9 sock.listen(0)
10
11 sleep_sec = 10
12 while True:
13 client_sock, addr = sock.accept()
14 print "New client:", client_sock.getpeername()
15 time.sleep(sleep_sec)
16
17 data = client_sock.recv(1024)
18 print "received:", len(data)
19
20 if __name__ == '__main__':
21 main()
client.py代码如下:
1 import socket
2 import time
3
4 def main():
5 host = ""
6 port = 7890
7 sock = socket.socket()
8 sock.connect((host, port))
9 print "connect successfully."
10
11 sent_byte = sock.send(' '*1024)
12 print "sent byte:", sent_byte
13 sock.close()
14
15 if __name__ == '__main__':
16 main()
4 实验结果
4.1 超出队列长度的客户端握手失败
当启动的客户端数量超过队列长度时,客户端与服务器端的连接实际上是没有建立起来的。通过命令“netstat -tuan |grep 7890”观察连接建立情况,如下所示。同时开启5个客户端程序,只有3个是与服务器建立连接的,它们的端口号是40695,40696,40697。而另外两个客户端与服务器的三次握手处于SYN_RECV状态,它们的端口号是40698,40699。
[root@roy network]# netstat -tuan |grep 7890
tcp 0 0 0.0.0.0:7890 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:7890 127.0.0.1:40699 SYN_RECV
tcp 0 0 127.0.0.1:7890 127.0.0.1:40698 SYN_RECV
tcp 0 1024 127.0.0.1:40698 127.0.0.1:7890 ESTABLISHED
tcp 1024 0 127.0.0.1:7890 127.0.0.1:40695 ESTABLISHED
tcp 0 0 127.0.0.1:40696 127.0.0.1:7890 ESTABLISHED
tcp 0 0 127.0.0.1:40695 127.0.0.1:7890 ESTABLISHED
tcp 0 0 127.0.0.1:40697 127.0.0.1:7890 ESTABLISHED
tcp 1024 0 127.0.0.1:7890 127.0.0.1:40697 ESTABLISHED
tcp 0 1024 127.0.0.1:40699 127.0.0.1:7890 ESTABLISHED
tcp 1024 0 127.0.0.1:7890 127.0.0.1:40696 ESTABLISHED
但是,这里应该注意到客户端认为的连接状态是成功建立的,因为从客户端端口到服务器端口的连接都是处于ESTABLISHED的。这将导致客户端程序执行connect时是成功返回的,并继续下一步的动作,向服务器发送数据。由于数据的发送是交给系统底层完成的,当客户端执行send时,数据将传送给系统底层。如果系统底层可以接收所有数据,则客户程序认为发送成功并返回。这点通过客户端执行结果,可以得到验证。所有客户端都是立即完成,并输出以下信息:
connect successfully.
sent byte: 1024
实际的情况是什么样的呢?通过命令“tcpdump -i lo”观察数据的传送。从以下数据可以看出,服务器程序实际上并没有与客户端程序建立连接,而且数据传输也没有真正完成。
18:20:06.566665 IP localhost.localdomain.40695 > localhost.localdomain.7890: Flags [S], seq 1216469746, win 32792, options [mss 16396,sackOK,TS val 33817848 ecr 0,nop,wscale 7], length 0
18:20:06.566681 IP localhost.localdomain.7890 > localhost.localdomain.40695: Flags [S.], seq 210636462, ack 1216469747, win 32768, options [mss 16396,sackOK,TS val 33817848 ecr 33817848,nop,wscale 7], length 0
18:20:06.566691 IP localhost.localdomain.40695 > localhost.localdomain.7890: Flags [.], ack 1, win 257, options [nop,nop,TS val 33817848 ecr 33817848], length 0 注:40695与7890三次握手完成。
18:20:06.566724 IP localhost.localdomain.40695 > localhost.localdomain.7890: Flags [P.], seq 1:1025, ack 1, win 257, options [nop,nop,TS val 33817848 ecr 33817848], length 1024
18:20:06.566729 IP localhost.localdomain.7890 > localhost.localdomain.40695: Flags [.], ack 1025, win 272, options [nop,nop,TS val 33817848 ecr 33817848], length 0 注:40695与7890数据传输完成。
18:20:07.958287 IP localhost.localdomain.40696 > localhost.localdomain.7890: Flags [S], seq 35163015, win 32792, options [mss 16396,sackOK,TS val 33819240 ecr 0,nop,wscale 7], length 0
18:20:07.958303 IP localhost.localdomain.7890 > localhost.localdomain.40696: Flags [S.], seq 3952522703, ack 35163016, win 32768, options [mss 16396,sackOK,TS val 33819240 ecr 33819240,nop,wscale 7], length 0
18:20:07.958314 IP localhost.localdomain.40696 > localhost.localdomain.7890: Flags [.], ack 1, win 257, options [nop,nop,TS val 33819240 ecr 33819240], length 0 注:40696与7890三次握手完成。
18:20:07.958350 IP localhost.localdomain.40696 > localhost.localdomain.7890: Flags [P.], seq 1:1025, ack 1, win 257, options [nop,nop,TS val 33819240 ecr 33819240], length 1024
18:20:07.958355 IP localhost.localdomain.7890 > localhost.localdomain.40696: Flags [.], ack 1025, win 272, options [nop,nop,TS val 33819240 ecr 33819240], length 0 注:40696与7890数据传输完成。
18:20:09.192664 IP localhost.localdomain.40697 > localhost.localdomain.7890: Flags [S], seq 3608836259, win 32792, options [mss 16396,sackOK,TS val 33820474 ecr 0,nop,wscale 7], length 0
18:20:09.192679 IP localhost.localdomain.7890 > localhost.localdomain.40697: Flags [S.], seq 685598874, ack 3608836260, win 32768, options [mss 16396,sackOK,TS val 33820474 ecr 33820474,nop,wscale 7], length 0
18:20:09.192689 IP localhost.localdomain.40697 > localhost.localdomain.7890: Flags [.], ack 1, win 257, options [nop,nop,TS val 33820474 ecr 33820474], length 0 注:40697与7890三次握手完成。
18:20:09.192720 IP localhost.localdomain.40697 > localhost.localdomain.7890: Flags [P.], seq 1:1025, ack 1, win 257, options [nop,nop,TS val 33820474 ecr 33820474], length 1024
18:20:09.192725 IP localhost.localdomain.7890 > localhost.localdomain.40697: Flags [.], ack 1025, win 272, options [nop,nop,TS val 33820474 ecr 33820474], length 0 注:40697与7890数据传输完成。
18:20:11.029180 IP localhost.localdomain.40698 > localhost.localdomain.7890: Flags [S], seq 2974930268, win 32792, options [mss 16396,sackOK,TS val 33822311 ecr 0,nop,wscale 7], length 0
18:20:11.029194 IP localhost.localdomain.7890 > localhost.localdomain.40698: Flags [S.], seq 1793008575, ack 2974930269, win 32768, options [mss 16396,sackOK,TS val 33822311 ecr 33822311,nop,wscale 7], length 0
18:20:11.029205 IP localhost.localdomain.40698 > localhost.localdomain.7890: Flags [.], ack 1, win 257, options [nop,nop,TS val 33822311 ecr 33822311], length 0 注:40698认为完成三次握手,开始发送数据
18:20:11.029235 IP localhost.localdomain.40698 > localhost.localdomain.7890: Flags [P.], seq 1:1025, ack 1, win 257, options [nop,nop,TS val 33822311 ecr 33822311], length 1024
18:20:11.234022 IP localhost.localdomain.40698 > localhost.localdomain.7890: Flags [P.], seq 1:1025, ack 1, win 257, options [nop,nop,TS val 33822516 ecr 33822311], length 1024
18:20:11.644014 IP localhost.localdomain.40698 > localhost.localdomain.7890: Flags [P.], seq 1:1025, ack 1, win 257, options [nop,nop,TS val 33822926 ecr 33822311], length 1024
18:20:12.464030 IP localhost.localdomain.40698 > localhost.localdomain.7890: Flags [P.], seq 1:1025, ack 1, win 257, options [nop,nop,TS val 33823746 ecr 33822311], length 1024 注:40698不断地尝试发送数据,但是一直得不到确认。
18:20:12.630848 IP localhost.localdomain.40699 > localhost.localdomain.7890: Flags [S], seq 636250882, win 32792, options [mss 16396,sackOK,TS val 33823912 ecr 0,nop,wscale 7], length 0
18:20:12.630869 IP localhost.localdomain.7890 > localhost.localdomain.40699: Flags [S.], seq 3213239187, ack 636250883, win 32768, options [mss 16396,sackOK,TS val 33823912 ecr 33823912,nop,wscale 7], length 0
18:20:12.630880 IP localhost.localdomain.40699 > localhost.localdomain.7890: Flags [.], ack 1, win 257, options [nop,nop,TS val 33823912 ecr 33823912], length 0 注:40699认为完成三次握手,开始发送数据
18:20:12.630906 IP localhost.localdomain.40699 > localhost.localdomain.7890: Flags [P.], seq 1:1025, ack 1, win 257, options [nop,nop,TS val 33823912 ecr 33823912], length 1024
18:20:12.835057 IP localhost.localdomain.40699 > localhost.localdomain.7890: Flags [P.], seq 1:1025, ack 1, win 257, options [nop,nop,TS val 33824117 ecr 33823912], length 1024
18:20:13.245181 IP localhost.localdomain.40699 > localhost.localdomain.7890: Flags [P.], seq 1:1025, ack 1, win 257, options [nop,nop,TS val 33824527 ecr 33823912], length 1024
18:20:14.065176 IP localhost.localdomain.40699 > localhost.localdomain.7890: Flags [P.], seq 1:1025, ack 1, win 257, options [nop,nop,TS val 33825347 ecr 33823912], length 1024 注:40699不断地尝试发送数据,但是一直得不到确认。
18:20:14.104004 IP localhost.localdomain.40698 > localhost.localdomain.7890: Flags [P.], seq 1:1025, ack 1, win 257, options [nop,nop,TS val 33825386 ecr 33822311], length 1024
18:20:15.429005 IP localhost.localdomain.7890 > localhost.localdomain.40698: Flags [S.], seq 1793008575, ack 2974930269, win 32768, options [mss 16396,sackOK,TS val 33826711 ecr 33825386,nop,wscale 7], length 0
18:20:15.429031 IP localhost.localdomain.40698 > localhost.localdomain.7890: Flags [.], ack 1, win 257, options [nop,nop,TS val 33826711 ecr 33826711,nop,nop,sack 1 {0:1}], length 0
18:20:15.705011 IP localhost.localdomain.40699 > localhost.localdomain.7890: Flags [P.], seq 1:1025, ack 1, win 257, options [nop,nop,TS val 33826987 ecr 33823912], length 1024
18:20:16.229023 IP localhost.localdomain.7890 > localhost.localdomain.40699: Flags [S.], seq 3213239187, ack 636250883, win 32768, options [mss 16396,sackOK,TS val 33827511 ecr 33826987,nop,wscale 7], length 0
18:20:16.229048 IP localhost.localdomain.40699 > localhost.localdomain.7890: Flags [.], ack 1, win 257, options [nop,nop,TS val 33827511 ecr 33827511,nop,nop,sack 1 {0:1}], length 0
18:20:17.384006 IP localhost.localdomain.40698 > localhost.localdomain.7890: Flags [P.], seq 1:1025, ack 1, win 257, options [nop,nop,TS val 33828666 ecr 33826711], length 1024
18:20:18.985134 IP localhost.localdomain.40699 > localhost.localdomain.7890: Flags [P.], seq 1:1025, ack 1, win 257, options [nop,nop,TS val 33830267 ecr 33827511], length 1024
18:20:21.829012 IP localhost.localdomain.7890 > localhost.localdomain.40698: Flags [S.], seq 1793008575, ack 2974930269, win 32768, options [mss 16396,sackOK,TS val 33833111 ecr 33828666,nop,wscale 7], length 0
18:20:21.829038 IP localhost.localdomain.40698 > localhost.localdomain.7890: Flags [.], ack 1, win 257, options [nop,nop,TS val 33833111 ecr 33833111,nop,nop,sack 1 {0:1}], length 0
18:20:22.628995 IP localhost.localdomain.7890 > localhost.localdomain.40699: Flags [S.], seq 3213239187, ack 636250883, win 32768, options [mss 16396,sackOK,TS val 33833911 ecr 33830267,nop,wscale 7], length 0
18:20:22.629018 IP localhost.localdomain.40699 > localhost.localdomain.7890: Flags [.], ack 1, win 257, options [nop,nop,TS val 33833911 ecr 33833911,nop,nop,sack 1 {0:1}], length 0
18:20:23.944009 IP localhost.localdomain.40698 > localhost.localdomain.7890: Flags [P.], seq 1:1025, ack 1, win 257, options [nop,nop,TS val 33835226 ecr 33833111], length 1024
18:20:25.545035 IP localhost.localdomain.40699 > localhost.localdomain.7890: Flags [P.], seq 1:1025, ack 1, win 257, options [nop,nop,TS val 33836827 ecr 33833911], length 1024
4.2 不同backlog对应的队列长度
5.总结
客户端connect成功返回时,并不表示与服务端的连接已经真正建立。Send发送数据成功返回也不表示服务器端已经成功接收了。编程时应该注意到这两点。