这一章节是对上一章节的验证。上一章节讲到了单进程、单线程非堵塞实现并发的原理。主要关键点就是设置非堵塞和定义一个空列表。向列表里面添加客户端。但是这个地方有一个严重的问题,就是客户端添加到列表之后,后面每一次遍历都会将列表里面的客户端全部都遍历一遍,即使这个客户端已经断开连接了,这样会耗费大量资源和精力。那么此时将断开的客户端从列表里面删除掉就是关键点了。此时只需要添加一个判断即可。
因为当关闭客户端的时候,客户端发送的数据为空,此时只要判断服务器接收到客户端发送的数据为空的时候,删除掉列表里面的客户端即可。具体的代码实现如下:
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("...客户端关闭...")