import logging import socket import threading from models.msg import Msg class TcpSer(threading.Thread): # 定义服务器地址和端口 HOST = '127.0.0.1' PORT = 2230 def __init__(self,host,port): super().__init__() self.HOST=host self.PORT=port self.connected_clients=[] # 消费者 self.consumers=[] self.lock = threading.Lock() self.running = True # self.daemon = True # 处理客户端连接的函数 def handle_client(self,client_socket:socket): # 保持连接,直到客户端断开 while self.running: # 保持连接,直到客户端断开 try: # 接收客户端数据(如果需要) data = client_socket.recv(1024*8) msg_str=data.decode('utf-8') if not data: break # 如果没有数据,退出循环 logging.info(f"从 {client_socket.getpeername()} 收到: {msg_str}") # 按 \n\n (十六进制 0a 0a) 分割消息 msg_str_list = msg_str.split('\n\n') for msg_str in msg_str_list: if msg_str=='\n\n' or len(msg_str)==0: continue if len(msg_str)<10: logging.info(f"无效消息: |{msg_str}|, 十六进制: {msg_str.encode('utf-8').hex()}") continue # 反序列化为 实例 s_cmd = Msg.from_json(msg_str) valid_msg: bool = True match s_cmd.cmd: case "getBase": self.on_data(s_cmd, valid_msg) case "getPoints" | "setPoints": self.on_data(s_cmd, valid_msg) case "getDataFps" | "setDataFps": self.on_data(s_cmd, valid_msg) case "setCap": self.on_data(s_cmd, valid_msg) case "videoMode": self.on_data(s_cmd, valid_msg) case "clearZero": self.on_data(s_cmd, valid_msg) case "getId" | "setId": self.on_data(s_cmd, valid_msg) case "getAlert" | "setAlert": self.on_data(s_cmd, valid_msg) case "getWin" | "setWin": self.on_data(s_cmd, valid_msg) case "getMqtt" | "setMqtt": self.on_data(s_cmd, valid_msg) # todo 添加处理 case _: valid_msg = False err_msg = f"valid cmd={s_cmd.cmd}" resp = f"""{{"_from": "dev","cmd": "{s_cmd.cmd}","values": {{"operate": false,"err": "{err_msg}"}}}}""" resp += "\n\n" client_socket.sendall(resp.encode()) logging.warn(f"非法命令 {resp}") logging.info("通讯完成") except ConnectionAbortedError: logging.warn("客户端已断开") self.clear_client(client_socket) return except Exception as ex: err_msg = f"illegal content" resp = f"""{{"_from": "dev","cmd": "","values": {{"operate": false,"err": "{err_msg}"}}}}""" resp += "\n\n" client_socket.sendall(resp.encode()) logging.warn(f"处理客户端命令出错: {str(ex)},非法命令{msg_str}") self.clear_client(client_socket) return finally: pass # 注册的消费者必须携带on_data 方法 def add_subscribe(self,consumer): if hasattr(consumer, 'on_data'): print(f"加入 consumer {consumer} ") self.consumers.append(consumer) else: print("consumer 缺少on_data函数,订阅无效 ") def on_data(self, msg, valid): for consumer in self.consumers: try: resp=consumer.on_data(msg) self.broadcast_message(resp) except Exception as e: logging.warn("通讯异常",e) # 广播消息给所有连接的客户端 def broadcast_message(self,msg:str): with self.lock: if len(msg)==0: return msg+="\n\n" for client in self.connected_clients: try: client.sendall(msg.encode()) except Exception as e: # 如果发送失败,从列表中移除客户端 if client in self.connected_clients: self.connected_clients.remove(client) client.close() print(f"向客户端发送消息时出错: {e}") def clear_client(self,client:socket): try: if client in self.connected_clients: self.connected_clients.remove(client) client.close() except Exception as e: logging.warn(f"清理客户端,异常: {e}") def run(self): # 创建服务器套接字 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server_socket: server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server_socket.bind((self.HOST,self.PORT)) server_socket.listen() server_socket.settimeout(1.0) # 设置 accept 超时时间为 1 秒 print(f"服务器监听在 {self.HOST}:{self.PORT}...") try: # 保持服务器运行并接受新的连接 while self.running: try: client_socket, addr = server_socket.accept() print(f"连接来自 {addr}") # 当客户端连接时,将其添加到列表中 self.connected_clients.append(client_socket) # 为每个客户端启动一个线程 client_thread = threading.Thread(target=self.handle_client, args=(client_socket,)) client_thread.daemon = True # 守护线程,服务器关闭时自动结束 client_thread.start() except socket.timeout: continue # 超时继续循环 except KeyboardInterrupt: print("服务器关闭...") finally: # 关闭所有客户端连接 for client in self.connected_clients: print(f"断开客户端 {client_socket.getpeername()}") client.close() server_socket.close() def stop(self): """外部调用此方法停止服务""" self.running = False if __name__ == '__main__': tcp=TcpSer("127.0.0.1",2230) tcp.run()