这一章节是对上一章节的验证。上一章节讲到了单进程、单线程非堵塞实现并发的原理。主要关键点就是设置非堵塞和定义一个空列表。向列表里面添加客户端。但是这个地方有一个严重的问题,就是客户端添加到列表之后,后面每一次遍历都会将列表里面的客户端全部都遍历一遍,即使这个客户端已经断开连接了,这样会耗费大量资源和精力。那么此时将断开的客户端从列表里面删除掉就是关键点了。此时只需要添加一个判断即可。
因为当关闭客户端的时候,客户端发送的数据为空,此时只要判断服务器接收到客户端发送的数据为空的时候,删除掉列表里面的客户端即可。具体的代码实现如下:
import socket
import time
tcp_socket_tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcp_socket_tcp.bind(("", 7780))
tcp_socket_tcp.listen(128)
tcp_socket_tcp.setblocking(False)
client_socket_list = list()
while True:
time.sleep(3)
try:
new_socket, new_addr = tcp_socket_tcp.accept()
# 等待客户端的到来,只要没有客户端连接,这个地方就会堵塞。因为设置了非堵塞,accept
# 返回值为空。那么这个地方会就产生异常。只要产生异常,则说明这个地方就还没有新的客户端连接
# 只要没有产生异常,则accept就有返回值,说明有新的客户端连接。
except Exception as ret:
print("...没有新的客户端到来...")
else:
print("...只要没有产生异常,那么意味着,来了一个新的客户端...")
# 产生的新的套接字new_socket,一旦调用recv的时候,立马就会堵塞。那么此时也应该将新的套接字new_socket
# 设置为非堵塞。那么一旦调用recv,如果没有数据到来,就会产生异常。反过来,只要不产生异常,就说明客户端
# 发送过来了数据了。
new_socket.setblocking(False) # 设置套接字为非堵塞
client_socket_list.append(new_socket)
# 新产生的套接字是接收客户端是否产生数据,因此这个套接字的接收信息循环程序不能放在里面。
# 因为一旦监听套接字没有新的客户端到来,就会产生异常,只要主程序产生异常,新产生的套接字就
# 不会运行,此时必挂。那么此时可以在外面定义一个空列表。将新的套接字添加到列表中,然后
# for循环遍历列表。只要new_socket 在列表中,则就会一直循环接收数据。
# 如果产生新的套接字,列表中就会有两个套接字,则遍历列表中两个套接字是否产生数据。这样就实现了
# 单进程、单线程检测多个套接字,也就实现了单进程和单线程实现http服务器。
for client_socket in client_socket_list:
try:
recv_data = client_socket.recv(1024)
except Exception as ret:
print("...这个客户端没有发送过来数据...")
else:
print("...没有异常...")
if recv_data:
# 只要接收到了数据,那么就可以进行其他的一些操作了。
print("...客户端发送过来了数据...")
print(recv_data)
# 对方调用了close导致recv返回
else:
# 客户端发送的数据为空,此时删除列表里面关闭的客户端。
client_socket.close()
client_socket_list.remove(client_socket)
print("...客户端关闭...")