Python实现串口转多客户端TCP服务器:客户端断开时如何更新套接字列表?
串口转多客户端TCP桥接:处理客户端断开连接的问题
你已经实现了一个串口转TCP/IP的桥接程序,能正常运行,但需要解决TCP客户端断开时更新SerialToNet类中sockets列表的问题。下面是修复后的完整代码,以及关键修改说明:
修复后的代码
import sys import socket from threading import Thread, Lock import serial import serial.threaded class SerialToNet(serial.threaded.Protocol): """serial->socket""" def __init__(self): self.sockets: list[socket.socket] = [] self.socket_lock = Lock() # 线程锁,保证多线程操作sockets列表安全 def __call__(self): return self def data_received(self, data): """Forward data from Serial to IP client Sockets""" with self.socket_lock: # 遍历列表副本,避免遍历过程中列表被修改导致异常 for socket in list(self.sockets): try: socket.sendall(data) except (ConnectionResetError, BrokenPipeError): # 发送失败说明客户端已断开,直接移除并关闭socket self.sockets.remove(socket) socket.close() class NetToSerial(Thread): """socket->serial""" def __init__(self, client_socket, serial_worker, serial_to_net): Thread.__init__(self) self._socket = client_socket self.serial_worker = serial_worker self.serial_to_net = serial_to_net def run(self): try: while True: data = self._socket.recv(1024) if not data: # recv返回空字节表示客户端主动断开连接 break self.serial_worker.write(data) except (ConnectionAbortedError, ConnectionResetError): print("NetToSerial client disconnection") finally: # 无论正常退出还是异常,都移除socket并关闭 with self.serial_to_net.socket_lock: if self._socket in self.serial_to_net.sockets: self.serial_to_net.sockets.remove(self._socket) self._socket.close() print(f"Client socket closed and removed from list") if __name__ == "__main__": # Serial connection SERIAL_COM_PORT = 'COM9' try: ser = serial.Serial(SERIAL_COM_PORT, 115200, timeout=2) except serial.SerialException: sys.exit(f"Serial port {SERIAL_COM_PORT} is not available") serial_to_net = SerialToNet() serial_worker = serial.threaded.ReaderThread(ser, serial_to_net) serial_worker.start() # TCP Server server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server_socket.bind(('', 3490)) mythreads = [] try: # Wait new IP clients while True: server_socket.listen() print("Server: waiting TCP client connection") (client_socket, client_addr) = server_socket.accept() print(f"New client connected: {client_addr}") # New client net_to_serial_thread = NetToSerial(client_socket, serial_worker, serial_to_net) with serial_to_net.socket_lock: serial_to_net.sockets.append(client_socket) net_to_serial_thread.start() mythreads.append(net_to_serial_thread) except KeyboardInterrupt: print("Shutting down server...") # 程序退出时清理资源 with serial_to_net.socket_lock: for sock in serial_to_net.sockets: sock.close() for t in mythreads: t.join() serial_worker.stop() ser.close()
关键修改说明
- 添加线程锁:在
SerialToNet中加入socket_lock,避免多个线程同时修改sockets列表引发的并发异常。 - 客户端断开时主动移除socket:在
NetToSerial的run方法中,通过finally块确保无论客户端是主动断开还是异常断开,都能从sockets列表中移除对应连接并关闭socket。 - 串口转发时的异常处理:在
data_received中遍历sockets的副本,同时捕获发送失败的异常,直接移除失效连接,防止后续重复报错。 - 优化全局变量依赖:将
serial_worker和serial_to_net作为参数传入NetToSerial,避免全局变量导致的线程安全问题。 - 完善退出清理逻辑:捕获
KeyboardInterrupt后,主动关闭所有客户端连接、等待线程结束、停止串口线程并关闭串口,确保资源正常释放。
内容的提问来源于stack exchange,提问作者nbout
相关产品推荐
相关产品推荐

