浅析对 Socket.listen() 方法的理解

Python 中的 socket 通信逻辑如下图所示(图片来源网络)

socket通信逻辑

其中 listen() 方法接收一个 backlog 参数:backlog指定在拒绝连接之前,操作系统可以挂起的最大连接数量。

一开始我将 backlog 参数理解为服务端可以同时与多少个客户端进行通讯,但后来发现因为 socket 通讯的过程是单进程单线程的,同一时刻,服务端只能与一个客户端进行通讯。其余客户端能与服务端建立连接,但是发送的消息得不到服务端的应答。

而 backlog 参数正是决定在服务端与一个客户端进行通讯是,能有多少个客户端还可以与其建立连接。服务端将其余的连接请求,以队列的结构存储。每次通过 accept() 方法取队列中的第一个来建立连接,其余的连接请求在队列中挂起。

backlog 参数的值,就是队列的大小

可以通过以下代码进行测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 服务端
import socket

ip_port = ('0.0.0.0', 7777)
s = socket.socket()
s.bind(ip_port)
s.listen(1) # 这里将 backlog 参数的值设置 1 ,即将队列大小设置为 1 ,允许有一个连接被挂起
conn, addr = s.accept()
while True:
data = conn.recv(1024).decode()
if data == 'exit':
conn, addr = s.accept()
if data == 'die':
break
print('接收到客户端[ '+addr[0]+':'+str(addr[1])+' ]的消息:' + data)
conn.sendall('\n消息已收到\n'.encode())
s.close()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 客户端
import socket

ip_port = ('192.168.177.1', 7777)
s = socket.socket()
s.connect(ip_port)
while True:
data = input('请输入要发送的内容:\n')
s.sendall(data.encode())
message = s.recv(1024).decode()
print(message)
if data == 'exit':
break
s.close()

启动服务端,然后依次开启3个客户端,并进行发送消息测试,结果如下

测试结果

可以看到,因为 backlog 参数设置为了 1 ,其中第二个请求连接成功,但被挂起,而第三个请求无法连接。

所以,可以确定,backlog 参数决定的是队列的大小,即服务端与多个客户端进行通讯时可以有多少个连接请求被挂起,而不是服务端可以同时与多少个客户端进行通讯。

因为 socket 通讯是单进程单线程的,如果要实现服务端能同时与多个客户端进行通讯,那就需要采用多进程或者多线程的方式来解决。