树莓派Linux环境下Python3服务端程序无故终止问题排查
问题解答与代码修正
关于监听套接字的放置
监听套接字必须放在while循环外。当前代码每次循环都重新创建套接字、绑定端口、启动监听,这会导致不必要的资源开销,甚至可能因为前一次的套接字连接还处于TIME_WAIT状态,导致绑定失败(即使设置了SO_REUSEADDR)。正确的做法是:初始化套接字、绑定、监听只做一次,在循环内仅处理客户端连接的接收和数据交互。
程序意外退出的可能原因及修复
你推测的「日志写入未加try/except导致崩溃」是合理的,此外当前代码还有多处潜在问题会导致程序异常退出:
getmaxindex函数在读取文件失败时没有返回值,后续执行encode('utf-8')会触发AttributeError- 客户端收发逻辑中,
recv(2)返回的是bytes类型,直接和字符串"done"比较永远不成立,会导致客户端套接字无法正确关闭 setup函数中创建的监听套接字s是局部变量,出现异常时无法正确关闭,造成资源泄漏- 异常日志仅记录固定字符串,没有时间戳和具体异常信息,难以排查问题
修正后的代码
import socket import time from datetime import datetime def getmaxindex(): try: with open('/home/pi/pm25/fan.txt', 'r') as f: # 读取后去除空白字符,避免文件中存在换行/空格导致问题 stat = f.read().strip() return stat if stat in {'0','1','2','3','4','5'} else '0' except Exception as e: # 捕获所有异常并写入带时间戳的日志 log_error(f"读取max_index失败: {str(e)}") return '0' # 返回默认值,避免后续编码报错 def log_error(message): # 日志写入单独封装,增加异常捕获 try: timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") with open("/home/pi/pm25/socketexceptions.txt", 'a') as f: f.write(f"[{timestamp}] {message}\n") except Exception as e: # 即使日志写入失败,也不影响主程序运行 print(f"日志写入失败: {str(e)}") def main(): # 初始化监听套接字,放在循环外 s = None try: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.bind(("192.168.1.70", 5050)) s.listen(1) print("服务端已启动,等待客户端连接...") except Exception as e: log_error(f"套接字初始化/绑定失败: {str(e)}") return try: while True: index = getmaxindex() # 等待客户端连接 try: clientsocket, address = s.accept() print(f"已连接客户端: {address}") # 发送编码后的index clientsocket.send(index.encode('utf-8')) # 接收客户端返回,注意bytes和字符串的比较 rx = clientsocket.recv(4) # "done"是4个字节,调整接收长度 if rx.strip() == b"done": print("客户端已完成操作") clientsocket.close() except Exception as e: log_error(f"与客户端通信失败: {str(e)}") # 如果客户端套接字已创建,尝试关闭 if 'clientsocket' in locals(): try: clientsocket.close() except: pass time.sleep(5) except KeyboardInterrupt: print("程序被手动终止") finally: # 确保监听套接字关闭 if s: s.close() print("监听套接字已关闭") if __name__ == "__main__": main()
关键修改点说明
- 将套接字初始化、绑定、监听逻辑移到循环外,避免重复创建资源
- 封装
log_error函数,增加时间戳和异常详情,同时捕获日志写入的异常,防止因日志问题导致主程序崩溃 getmaxindex函数在读取失败时返回默认值'0',并校验返回值的合法性- 修正
recv的长度("done"是4字节),并使用bytes类型b"done"进行比较 - 增加
finally块确保监听套接字在程序退出时正确关闭 - 使用
main函数封装主逻辑,代码结构更清晰
额外建议
- 启动程序时,可以将输出重定向到单独的日志文件,方便排查:
nice nohup python3 server.py > /home/pi/pm25/server.log 2>&1 & - 可以使用系统服务(如systemd)管理程序,这样程序意外退出时会自动重启,比nohup更可靠
内容的提问来源于stack exchange,提问作者joelindo
相关产品推荐
相关产品推荐

