class SocketStatus(Enum):
"""连接状态枚举,提高代码可读性"""
OFFLINE = "Offline"
CONNECTING = "Connecting"
RECONNECT = "ReConnect"
RECONNECT_ERROR = "ReConnect_error"
CONNECT_WAIT_FOR_FLAG = "CONNECT_WAIT_FOR_FLAG"
CONNECT_WAIT_FOR_0X05 = "Connect_wait_for_0x05"
CONNECT_WAIT_FOR_0X06 = "Connect_wait_for_0x06"
CONNECT_WAIT_FOR_0X09 = "Connect_wait_for_0x09"
CONNECT_WAIT_FOR_0X0A = "Connect_wait_for_0x0A"
CONNECT_WAIT_FOR_0X13 = "Connect_wait_for_0x13"
CONNECT_WAIT_FOR_0X55 = "Connect_wait_for_0x55"
CONNECT_WAIT_FOR_0X56 = "Connect_wait_for_0x56"
ONLINE = "Online"
class AsyncSocketWriter:
"""异步Socket发送器 - 专门负责数据发送"""
def __init__(self, writer, pileCode, message_id, total, connections, logger):
self.writer = writer
self.pileCode = pileCode
self.message_id = message_id
self.start = " "
self.dataLen = " "
self.serialNum = ""
self.encryption = " "
self.Type = " "
self.pileType = " "
self.chargingGunNum = " "
self.protocolVersion = " "
self.programVersion = ""
self.netType = " "
self.SIMCard = " "
self.carrier = " "
self.modelCode = " "
self.charging = False
self.running = True
self.total = total
self.time_count = 0
self.logger = logger
self.connections = connections[pileCode]
self._write_lock = asyncio.Lock()
self._status_handlers = {
SocketStatus.OFFLINE: self._handle_offline_status,
SocketStatus.CONNECTING: self._handle_connecting_status,
SocketStatus.RECONNECT: self._handle_reconnect_status,
SocketStatus.RECONNECT_ERROR: self._handle_reconnect_error_status,
SocketStatus.CONNECT_WAIT_FOR_FLAG: self._handle_wait_for_flag,
SocketStatus.CONNECT_WAIT_FOR_0X05: self._handle_wait_for_0x05,
SocketStatus.CONNECT_WAIT_FOR_0X06: self._handle_wait_for_0x06,
SocketStatus.CONNECT_WAIT_FOR_0X09: self._handle_wait_for_0x09,
SocketStatus.CONNECT_WAIT_FOR_0X0A: self._handle_wait_for_0x0A,
SocketStatus.CONNECT_WAIT_FOR_0X13: self._handle_wait_for_0x13,
SocketStatus.CONNECT_WAIT_FOR_0X55: self._handle_wait_for_0x55,
SocketStatus.CONNECT_WAIT_FOR_0X56: self._handle_wait_for_0x56,
SocketStatus.ONLINE: self._handle_online_status
}
self.send_0x13_flag = False
async def start_write(self):
self.logger.info(f'✅Socket {self.message_id} 桩号: {self.pileCode} 正在等待发送报文序列')
while self.running:
try:
await self._process_status()
await asyncio.sleep(0.5)
except asyncio.CancelledError:
break
except Exception as e:
raise e
class AsyncSocketReader:
"""异步Socket读取器 - 专门负责数据接收"""
def __init__(self, reader, pileCode, message_id, connections, logger):
self.reader = reader
self.pileCode = pileCode
self.message_id = message_id
self.running = True
self.connections = connections[pileCode]
self._reader_lock = asyncio.Lock()
self.logger = logger
# 报文处理器映射
self._message_handlers = {
0x02: self._handle_login_message,
0x04: self._handle_status_message,
0x06: self._handle_billing_model_message,
0x56: self._handle_unknown_message_56,
0x0A: self._handle_price_model_message,
0x9C: self._handle_unknown_message_9C
}
self.flag_0x0A = False
self.flag_0x56 = False
self.flag_0x13 = False
async def start_reading(self):
global logger
while self.running:
try:
await self._read_and_process()
await asyncio.sleep(0.5)
except Exception as e:
raise e
class AsyncSocketManager:
"""异步Socket管理器 - 协调Reader和Writer线程"""
def __init__(self):
self.running = True
self._shutdown_event = asyncio.Event()
self._cleanup_lock = asyncio.Lock()
self.shutdown_requested = asyncio.Event()
self.original_sigint_handler = None
self.shutdown_requested = asyncio.Event()
self.server_config = {'ip': 'xxx.com', 'port': 5455}
self.total = 0
self.writer = None
self.reader = None
self._active_tasks = set()
self.connections: Dict[str, Dict] = {}
self.tasks: List[asyncio.Task] = []
self._read_lock = asyncio.Lock() # 读取操作专用锁
self._write_lock = asyncio.Lock() # 写入操作专用锁
self._state_lock = asyncio.Lock() # 状态变更锁
self.logger = logging.getLogger(__name__)
self.setup_logging()
self.connections_count = 0
async def socket_worker(self, message_id: str, pileCode: str, total: int):
global logger
"""异步Socket工作协程"""
current_times = 0
while self.running and not self._shutdown_event.is_set():
try:
writer, reader = await self.create_socket_connection(self.server_config, message_id, pileCode)
if writer is not None and reader is not None:
self.writer, self.reader = AsyncSocketWriter(writer, pileCode, message_id, self.total,
self.connections, self.logger), AsyncSocketReader(
reader, pileCode, message_id, self.connections, self.logger)
if current_times == 0:
activeNum = len(list(filter(lambda conn: conn.get('isLogin', False), self.connections.values())))
self.logger.info(f'🌍 现有连接 {activeNum} / {self.total}')
current_times = time.time()
elif time.time() - current_times > 6:
activeNum = len(list(filter(lambda conn: conn.get('isLogin', False), self.connections.values())))
self.logger.info(f'🌍 现有连接 {activeNum} / {self.total}')
current_times = time.time()
if self.writer is not None and self.reader is not None:
# 并发启动读写任务
read_task = asyncio.create_task(
self.reader.start_reading(),
name=f"reader_{pileCode}_{message_id}"
)
write_task = asyncio.create_task(
self.writer.start_write(),
name=f"writer_{pileCode}_{message_id}"
)
# 添加到实例级别的活跃任务集合
self._active_tasks.add(read_task)
self._active_tasks.add(write_task)
await asyncio.gather(
read_task,
write_task,
return_exceptions=True
)
except asyncio.CancelledError:
self.logger.info("Socket工作协程被取消")
except Exception as e:
self.logger.error(f"❌Socket {message_id} 桩号: {pileCode} 工作线程异常: {e}")
if pileCode in self.connections:
if self.connections[pileCode]['writer'] is not None:
try:
self.connections[pileCode]['writer'].close()
await self.connections[pileCode]['writer'].wait_closed()
except Exception as close_error:
self.logger.error(
f"❌ 发生错误 {e} 而关闭Socket {message_id} 桩号: {pileCode} 失败: {close_error}")
del self.connections[pileCode]
self.logger.info(f"✅发生错误 {e} 原因Socket {message_id} 桩号: {pileCode} 连接已关闭")
await asyncio.sleep(20) # 异常后等待20秒重试
except KeyboardInterrupt:
self.logger.error("接收到中断信号...")
break
async def start_multiple_connections(self, piles_list_config):
"""异步启动多个Socket连接"""
for i, pileCode in enumerate(piles_list_config):
task = asyncio.create_task(
self.socket_worker(
f"conn_{i + 1}", pileCode, len(piles_list_config))
)
self.total = len(piles_list_config)
self.tasks.append(task)
self.logger.info(
f"启动异步Socket连接 {i + 1} 到 {self.server_config['ip']}:{self.server_config['port']} 桩号: {pileCode}")
async def create_socket_connection(self, server_config: Dict, message_id: str, pileCode: str):
global logger
"""创建单个socket连接并处理通信"""
try:
"""创建异步Socket连接"""
reader, writer = await asyncio.wait_for(
asyncio.open_connection(server_config['ip'], server_config['port']),
timeout=60.0
)
self.logger.info(
f"异步Socket {message_id} 桩号: {pileCode} "
f"成功连接到 {server_config['ip']}:{server_config['port']}"
)
# 存储连接信息
self.connections[pileCode] = {
'reader': reader,
'writer': writer,
'pileCode': pileCode,
'message_id': message_id,
'config': server_config,
'last_heartbeat1': 0,
'last_heartbeat2': 0,
'last_time_stamp': 0,
'isLogin': False,
'timeout_count_login': 0,
'timeout_count': 0,
'heart_serialNum': "00 00",
'status': 'Offline',
'charging': False,
'priceModelList': []
}
return writer, reader
except asyncio.TimeoutError:
if pileCode in self.connections:
if self.connections[pileCode]['writer'] is not None:
try:
self.connections[pileCode]['writer'].close()
await self.connections[pileCode]['writer'].wait_closed()
except Exception as close_error:
self.logger.error(f"❌ 超时而关闭Socket {message_id} 桩号: {pileCode} 失败: {close_error}")
if pileCode in self.connections:
del self.connections[pileCode]
self.logger.info(f"✅超时原因 Socket {message_id} 桩号: {pileCode} 连接已关闭")
self.logger.error(f"⛔Socket {message_id} 桩号: {pileCode} 连接超时")
except ConnectionRefusedError:
if pileCode in self.connections:
if self.connections[pileCode]['writer'] is not None:
try:
self.connections[pileCode]['writer'].close()
await self.connections[pileCode]['writer'].wait_closed()
except Exception as close_error:
self.logger.error(f"❌ 连接拒绝而关闭Socket {message_id} 桩号: {pileCode} 失败: {close_error}")
if pileCode in self.connections:
del self.connections[pileCode]
self.logger.info(f"✅连接拒绝原因 Socket {message_id} 桩号: {pileCode} 连接已关闭")
self.logger.error(f"🚫Socket {message_id} 桩号: {pileCode} 连接被拒绝")
except Exception as e:
if pileCode in self.connections:
if self.connections[pileCode]['writer'] is not None:
try:
self.connections[pileCode]['writer'].close()
await self.connections[pileCode]['writer'].wait_closed()
except Exception as close_error:
self.logger.error(
f"❌ 发生错误 {e} 而关闭Socket {message_id} 桩号: {pileCode} 失败: {close_error}")
if pileCode in self.connections:
del self.connections[pileCode]
self.logger.info(f"❌发生错误 {e} 原因Socket {message_id} 桩号: {pileCode} 连接已关闭")
self.logger.error(f"❌Socket {pileCode} 桩号: {pileCode} 发生错误: {e}")
except KeyboardInterrupt:
self.logger.info("接收到中断信号...")
def read_to_array(file_path, remove_empty=True, encoding='utf-8'):
"""
读取txt文件到数组
参数:
file_path: 文件路径
remove_empty: 是否移除空行
encoding: 文件编码
"""
try:
with open(file_path, 'r', encoding=encoding) as file:
if remove_empty:
lines = [line.strip() for line in file if line.strip()]
else:
lines = [line.strip() for line in file]
# 对每行数据进行处理
processed_lines = []
for line in lines:
if len(line) == 14 and line.isdigit():
# 将14位数字分割为每2位一组
parts = [line[i:i + 2] for i in range(0, 14, 2)]
processed_line = ' '.join(parts)
processed_lines.append(processed_line)
else:
processed_lines.append(line)
return processed_lines
except FileNotFoundError:
print(f"❌错误: 文件 '{file_path}' 不存在")
return []
except UnicodeDecodeError:
print(f"❌错误: 使用 {encoding} 编码读取文件失败")
return []
except Exception as e:
print(f"❌未知错误: {e}")
return []
async def async_main():
# 定义多个桩号配置
piles_list_config = read_to_array('pileList.txt')
if not piles_list_config:
print("⚠️ 桩号配置文件为空或无有效数据")
return
# 创建多socket客户端实例
manager = AsyncSocketManager()
# 创建优雅关闭管理器
manager.install_signal_handlers()
try:
# 启动多个连接
print("启动多个socket连接...")
await manager.start_multiple_connections(piles_list_config)
# 使用事件等待替代while循环
logger.info("✅ 所有连接已启动,等待工作完成或关闭信号...")
await manager.shutdown_requested.wait()
logger.info("🔄 接收到关闭信号,开始清理流程...")
except asyncio.CancelledError:
logger.info("异步任务被取消")
# 重新抛出以便上层处理
raise "异步任务被取消"
except KeyboardInterrupt:
logger.info("接收到键盘中断信号")
# 不直接处理,让信号处理器处理
except Exception as e:
logger.error(f"❌ 异步Socket客户端异常: {e}")
finally:
# 确保资源清理
logger.info("开始最终资源清理...")
await manager.close_all_connections()
manager.restore_signal_handlers()
logger.info("✅ 异步Socket客户端已完全关闭")
def main():
"""主入口函数"""
try:
asyncio.run(async_main())
except KeyboardInterrupt:
logger.info("程序被用户中断")
except Exception as e:
logger.error(f"程序异常退出: {e}")
return 1
return 0
if __name__ == "__main__":
exit(main())
优化以上代码,给出完整代码
最新发布