在Ubuntu 22.04 LTS上稳定运行Python TCP监听器的有效方法
问题描述
我有一个监听4995端口的Python TCP脚本,需要它始终运行、监听连接并将接收的数据保存到文件中。脚本代码如下:
import socket import threading from datetime import datetime import time class Testing: def __init__(self): self.PORT = 4995 self.SERVER = '' self.ADDR = (self.SERVER, self.PORT) self.count = 0 def handle_client(self, conn, addr): print(f'[{datetime.now().strftime("%H:%M")}][NEW CONNECTION] {addr} connected.') s = time.time() while True: if time.time() - s > 300: conn.close() print(f'[{datetime.now().strftime("%H:%M")}][CLOSE CONNECTION] {addr} closed.') break try: msg = conn.recv(2048).decode('utf-8') except ConnectionResetError: print(f'[{datetime.now().strftime("%H:%M")}][CLOSE CONNECTION] {addr} closed by peers.') break else: if msg: print(f'[{datetime.now().strftime("%H:%M")}] {msg}') with open('msg.txt','a') as file: file.write(msg) s = time.time() # conn.close() def start(self): self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.server.bind(self.ADDR) self.server.listen() print(f'[LISTENING] Server is listening on {self.SERVER}:{self.PORT}') while True: try: self.conn, addr = self.server.accept() except OSError: return thread = threading.Thread(target=self.handle_client, args=(self.conn, addr)) thread.start() print(f'[{datetime.now().strftime("%H:%M")}][ACTIVE CONNECTIONS] {threading.active_count() - 1}') if __name__ == '__main__': Testing().start()
我曾使用命令nohup python3 -u script.py &运行该脚本,初期可正常工作,但一段时间后客户端无法与服务器建立连接,而脚本本身仍在运行。经了解,nohup会因Linux系统资源情况导致脚本停止,现寻求让该脚本始终运行并监听连接的方法。
解决方案
一、先修复脚本潜在问题
脚本运行一段时间后无法接受新连接,不一定是nohup的问题,先排查并修复脚本本身的隐患:
- 处理客户端的线程未设置为守护线程,主线程意外退出时,子线程可能残留占用资源;
- 确保连接无论如何都会被关闭,避免套接字资源泄漏;
- 写入文件时使用绝对路径,防止脚本运行目录变化导致文件找不到,同时添加时间戳便于日志追溯。
修改后的关键代码片段:
def handle_client(self, conn, addr): print(f'[{datetime.now().strftime("%H:%M")}][NEW CONNECTION] {addr} connected.') s = time.time() try: while True: if time.time() - s > 300: print(f'[{datetime.now().strftime("%H:%M")}][CLOSE CONNECTION] {addr} closed (timeout).') break try: msg = conn.recv(2048).decode('utf-8') except ConnectionResetError: print(f'[{datetime.now().strftime("%H:%M")}][CLOSE CONNECTION] {addr} closed by peers.') break else: if msg: print(f'[{datetime.now().strftime("%H:%M")}] {msg}') # 替换为实际的绝对路径,确保目录已创建 with open('/var/tcp_listener/msg.txt','a') as file: file.write(f'[{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}] {msg}\n') s = time.time() finally: conn.close() print(f'[{datetime.now().strftime("%H:%M")}][CONNECTION CLOSED] {addr} socket released.') # 启动线程时设置为守护线程 thread = threading.Thread(target=self.handle_client, args=(self.conn, addr), daemon=True)
二、使用systemd服务托管脚本(推荐)
Linux下最可靠的持久化运行方式是用systemd将脚本注册为系统服务,实现开机自启、异常自动重启、资源监控:
- 创建systemd服务文件
/etc/systemd/system/tcp-listener.service:
[Unit] Description=TCP Listener Service (Port 4995) After=network.target [Service] User=your_username # 替换为运行脚本的用户(如普通用户或root) WorkingDirectory=/path/to/script/dir # 脚本所在目录的绝对路径 ExecStart=/usr/bin/python3 /path/to/script/dir/your_script.py # 脚本的绝对路径 Restart=always # 任何情况退出都自动重启 RestartSec=5 # 重启间隔5秒 StandardOutput=append:/var/log/tcp-listener.log # 标准日志输出文件 StandardError=append:/var/log/tcp-listener.error.log # 错误日志文件 [Install] WantedBy=multi-user.target
- 配置并启动服务:
# 重新加载systemd配置 sudo systemctl daemon-reload # 启动服务 sudo systemctl start tcp-listener.service # 设置开机自启 sudo systemctl enable tcp-listener.service
- 常用管理命令:
# 查看服务运行状态 sudo systemctl status tcp-listener.service # 实时查看服务日志 journalctl -u tcp-listener.service -f # 手动重启服务 sudo systemctl restart tcp-listener.service
三、其他替代方案
如果暂时不想用systemd,也可以用以下方式托管脚本:
1. 使用screen/tmux保持会话
- screen:
# 创建新会话 screen -S tcp_listener # 在会话内运行脚本 python3 your_script.py # 按Ctrl+A+D分离会话,脚本后台继续运行 # 重新连接会话 screen -r tcp_listener
- tmux:用法与screen类似,功能更丰富,是screen的现代替代工具。
2. 使用supervisor进程管理
安装supervisor后,创建配置文件/etc/supervisor/conf.d/tcp-listener.conf:
[program:tcp-listener] command=/usr/bin/python3 /path/to/your_script.py directory=/path/to/script/dir user=your_username autostart=true autorestart=true stdout_logfile=/var/log/tcp-listener.log stderr_logfile=/var/log/tcp-listener.error.log
更新配置并启动:
sudo supervisorctl reread sudo supervisorctl update sudo supervisorctl start tcp-listener
内容的提问来源于stack exchange,提问作者Priyansh Khandelwal
相关产品推荐
相关产品推荐

