feat:更新tcp服务器模块

This commit is contained in:
2025-10-26 22:47:13 +08:00
parent 9117c3ac1e
commit 1a84ab7770
2 changed files with 63 additions and 143 deletions

View File

@@ -1,161 +1,84 @@
import socket import asyncio
import signal import signal
import threading import platform
from concurrent.futures import ThreadPoolExecutor from config.logger_config import logger
from config.setting import settings from config.setting import settings
from model.index import UNKNOWN_MESSAGE from model.index import UNKNOWN_MESSAGE
from processor.tcp_processor import TcpMessageProcessor from processor.tcp_processor import TcpMessageProcessor
from config.logger_config import logger
class TCPServer: class TCPServer:
def __init__(self): def __init__(self):
self.host = settings.host self.host = settings.host
self.port = settings.port self.port = settings.port
self.server = None
self.max_workers = settings.max_workers self.clients = set()
self.timeout = settings.timeout
self.server_socket = None
self.running = False self.running = False
self.thread_pool = ThreadPoolExecutor(max_workers=self.max_workers)
self.message_processor = TcpMessageProcessor() self.message_processor = TcpMessageProcessor()
# 已经连接的客户端 async def handle_client(self, reader, writer):
self.clients = {}
self.client_lock = threading.Lock()
self.server_thread = None
signal.signal(signal.SIGTERM, self._handle_signal)
signal.signal(signal.SIGINT, self._handle_signal)
def _handle_signal(self, signum, frame):
"""处理终止信号,触发优雅关闭"""
logger.info(f"收到信号 {signum},准备关闭服务器...")
self.running = False
def _start_loop(self):
"""启动服务器"""
try:
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.server_socket.bind((self.host, self.port))
self.server_socket.listen(5)
self.server_socket.settimeout(1.0)
self.running = True
logger.info(f"TCP服务器启动监听 {self.host}:{self.port} "
f"(最大线程: {self.max_workers}, 超时: {self.timeout}s")
while self.running:
try:
client_socket, client_address = self.server_socket.accept()
client_ip = client_address[0]
client_socket.settimeout(self.timeout)
logger.info(f"新连接: {client_address}")
# 存储客户端
with self.client_lock:
self.clients[client_ip] = client_socket
# 提交到线程池处理
self.thread_pool.submit(self.handle_client, client_socket, client_ip)
except socket.timeout:
continue
except Exception as e:
if self.running:
logger.error(f"接受连接失败: {str(e)}")
except Exception as e:
logger.error(f"TCP服务器启动失败: {str(e)}")
finally:
self.stop()
def start(self):
self.server_thread = threading.Thread(target=self._start_loop, daemon=True)
self.server_thread.start()
def stop(self):
if not self.running:
return
self.running = False
logger.info("开始关闭服务器...")
# 移除客户端
with self.client_lock:
for client_socket in self.clients.values():
try:
client_socket.close()
except Exception as e:
logger.warning(f"关闭客户端连接失败: {str(e)}")
self.clients.clear()
# 关闭线程池
self.thread_pool.shutdown(wait=True)
logger.info("所有客户端处理线程已结束")
# 关闭连接
if self.server_socket:
self.server_socket.close()
logger.info(f"服务器已关闭({self.host}:{self.port}")
def handle_client(self, client_socket, client_ip):
"""处理客户端连接""" """处理客户端连接"""
addr = writer.get_extra_info('peername')
logger.info(f"新客户端连接: {addr}")
self.clients.add(writer)
try: try:
while True: while self.running:
data = client_socket.recv(1024) data = await reader.read(1024)
if not data: if not data:
logger.info(f"客户端 {client_ip} 主动断开连接") break # 连接断开
break
message = data.decode('utf-8').strip() message = data.decode('utf-8').strip()
logger.info(f"收到 {client_ip} 的消息: {message}") logger.info(f"收到 {addr} 的消息: {message}")
# 放到消息处理器里面处理 # 放到消息处理器里面处理
response = self.message_processor.process(message) response = self.message_processor.process(message)
# 回复消息 # 回复消息
if response != UNKNOWN_MESSAGE: if response != UNKNOWN_MESSAGE:
client_socket.sendall(response.encode('utf-8')) writer.write(response.encode())
logger.info(f"回复 {client_ip}: {response}") await writer.drain()
except socket.timeout: logger.info(f"回复 {addr}: {response}")
logger.warning(f"客户端 {client_ip} 超时未活动")
except Exception as e: except Exception as e:
logger.error(f"处理 {client_ip} 出错: {str(e)}") logger.error(f"客户端 {addr} 处理错误: {e}")
finally: finally:
# 异常情况下关闭连接 if writer in self.clients:
with self.client_lock: self.clients.remove(writer)
if client_ip in self.clients: writer.close()
del self.clients[client_ip] await writer.wait_closed()
logger.warning(f"客户端 {addr} 断开")
try: async def start(self):
client_socket.close() """启动服务器"""
logger.info(f"客户端 {client_ip} 连接已关闭") self.running = True
except Exception as e: self.server = await asyncio.start_server(self.handle_client, self.host, self.port)
logger.warning(f"关闭 {client_ip} 连接失败: {str(e)}")
def send_to_client(self, client_ip, message): # 仅在非Windows平台设置信号处理
# 先获取客户端连接(加锁保护) if platform.system() != 'Windows':
with self.client_lock: loop = asyncio.get_running_loop()
client_socket = self.clients.get(client_ip) for sig in (signal.SIGINT, signal.SIGTERM):
if not client_socket: loop.add_signal_handler(sig, self.stop)
logger.warning(f"客户端 {client_ip} 不存在或已断开连接")
return False
# 发送消息 logger.info(f"TCP服务器已启动 {self.host}:{self.port}")
try: async with self.server:
client_socket.sendall(message.encode('utf-8')) await self.server.serve_forever()
logger.info(f"主动发送消息给 {client_ip}: {message}")
return True async def _safe_stop(self):
except Exception as e: """安全的停止服务器"""
logger.error(f"{client_ip} 发送消息失败: {str(e)}") self.running = False
# 发送失败时移除无效连接 if self.server:
with self.client_lock: self.server.close()
if client_ip in self.clients: await self.server.wait_closed()
del self.clients[client_ip]
return False # 关闭所有客户端连接
for writer in list(self.clients):
writer.close()
await writer.wait_closed()
def stop(self):
"""停止服务器"""
logger.info("正在关闭TCP服务器...")
asyncio.create_task(self._safe_stop())
tcp_server = TCPServer() tcp_server = TCPServer()

25
main.py
View File

@@ -1,19 +1,16 @@
from endpoint.tcp_server import tcp_server import asyncio
from endpoint.tcp_server import TCPServer
from config.logger_config import logger from config.logger_config import logger
import time
if __name__ == "__main__": if __name__ == "__main__":
server = TCPServer()
try: try:
tcp_server.start() asyncio.run(server.start())
except KeyboardInterrupt:
try: logger.info("收到中断信号,正在关闭服务器...")
while True: server.stop()
time.sleep(1)
except KeyboardInterrupt:
logger.info("收到终止信号,开始关闭程序...")
finally:
tcp_server.stop()
logger.info("程序已退出")
except Exception as e: except Exception as e:
logger.critical(f"程序启动失败: {str(e)}", exc_info=True) logger.error(f"服务器运行错误: {e}")
exit(1) server.stop()
finally:
logger.info("服务器进程结束")