You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

树莓派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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 14:06:20