0

0

Python 多线程 Socket 服务器正确启动与主线程持续运行的实现方法

聖光之護

聖光之護

发布时间:2026-01-17 18:56:36

|

598人浏览过

|

来源于php中文网

原创

Python 多线程 Socket 服务器正确启动与主线程持续运行的实现方法

本文详解如何修复 python socket 服务器中因线程阻塞导致主线程无法继续执行的问题,重点说明 `thread.start()` 后主线程挂起的常见误区,并提供可稳定运行的多线程服务端模板。

在使用 threading.Thread 创建后台任务(如长循环的客户端通信)时,一个常见误解是:调用 thread.start() 就能“自动释放”主线程控制权。实际上,问题往往不出在 start() 本身,而在于目标函数是否被正确设计为非阻塞、可并发执行的逻辑,以及主线程是否因未做任何耗时/等待操作而意外退出或被覆盖

观察原始代码,核心问题有三处:

  1. stream() 方法无参数却依赖实例变量 self.client 和 self.message
    由于多个客户端连接会共享同一个 Server 实例,self.client 在新连接到来时被覆盖,导致前一个线程向已失效的 socket 发送数据,极易触发 BrokenPipeError 或静默卡死;同时 self.message == "" 导致发送空字节流,部分 TCP 可能不立即刷新缓冲区,造成“假死”现象。

  2. run() 中未对 stream() 线程设置守护属性(daemon=True),但主线程缺乏保活机制
    虽然 thread.start() 正确启动了子线程,但 test.py 中的 while True: print("A") 本应持续输出,却未生效——根本原因是:该循环运行极快,大量 print 输出被系统缓冲或终端渲染延迟掩盖;更关键的是,若 server.run() 所在线程因异常终止(如 socket 错误未捕获),而主线程又未做任何显式等待,整个进程可能因无活跃非守护线程而退出

  3. 缺少基础错误处理与资源管理
    原始代码未捕获 socket.error、ConnectionResetError 等常见网络异常,也未关闭 socket,易导致文件描述符泄漏和端口占用。

✅ 正确做法如下:

  • 为每个客户端分配独立线程,并传入专属 socket 对象:避免共享状态冲突;
  • 在 stream() 中添加心跳/延时(如 time.sleep(1)):防止高频发送压垮连接,也便于观察线程行为;
  • 主线程需主动维持运行(如 time.sleep())并捕获中断信号:确保其不因 CPU 占满或意外退出而终止;
  • 推荐将服务线程设为守护线程(daemon=True):使主程序退出时子线程自动结束,避免僵尸线程。

以下是优化后的完整可运行示例:

Imagine By Magic Studio
Imagine By Magic Studio

AI图片生成器,用文字制作图片

下载

立即学习Python免费学习笔记(深入)”;

# server.py
import socket
import threading
import time

class Server:
    def __init__(self, host: str, port: int):
        self.host = host
        self.port = port
        self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  # 允许端口重用
        self.server.bind((host, port))
        self.server.listen(5)  # 设置最大连接队列长度
        print(f"Server listening on {host}:{port}")

    def stream(self, client: socket.socket, address):
        """为单个客户端提供持续服务"""
        print(f"[+] New connection from {address}")
        try:
            while True:
                client.send(b"HELLO FROM SERVER\n")
                time.sleep(1)
        except (ConnectionResetError, BrokenPipeError, OSError) as e:
            print(f"[-] Client {address} disconnected: {e}")
        finally:
            client.close()

    def run(self):
        """主监听循环 —— 每接受一个连接即启动一个新线程"""
        try:
            while True:
                client, address = self.server.accept()
                # 为每个客户端创建独立线程,传入专属 socket
                thread = threading.Thread(
                    target=self.stream,
                    args=(client, address),
                    daemon=True  # 设为守护线程,主程序退出时自动终止
                )
                thread.start()
        except KeyboardInterrupt:
            print("\n[!] Server shutting down...")
        finally:
            self.server.close()
# test.py
from server import Server
import time

if __name__ == "__main__":
    # 绑定到所有接口('')以支持局域网连接
    server = Server('', 8001)

    # 启动服务线程
    server_thread = threading.Thread(target=server.run, daemon=False)
    server_thread.start()

    # 主线程持续运行,输出提示并响应 Ctrl+C
    print("Server started. Press Ctrl+C to stop.")
    try:
        while server_thread.is_alive():
            print(".", end="", flush=True)
            time.sleep(1)
    except KeyboardInterrupt:
        print("\n[!] Exiting...")

? 关键注意事项

  • ✅ 使用 daemon=True 可避免子线程阻止主程序退出,但需确保关键清理逻辑(如 server.close())放在 run() 的 finally 块中;
  • ✅ self.server.setsockopt(... SO_REUSEADDR ...) 防止重启时报 “Address already in use”;
  • ✅ client.send() 前无需 encode() 若直接发送 bytes 字面量(如 b"..."),语义更清晰;
  • ❌ 避免在 stream() 中修改共享实例变量(如 self.message),应通过参数传递或使用线程局部存储(threading.local)隔离状态。

运行后,你将看到 . 持续打印,同时终端显示连接日志;用 telnet 192.168.178.30 8001 或 Python socket 客户端连接即可收到连续的 HELLO FROM SERVER 消息——主线程与服务线程真正并行无阻。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
python中print函数的用法
python中print函数的用法

python中print函数的语法是“print(value1, value2, ..., sep=' ', end=' ', file=sys.stdout, flush=False)”。本专题为大家提供print相关的文章、下载、课程内容,供大家免费下载体验。

192

2023.09.27

python print用法与作用
python print用法与作用

本专题整合了python print的用法、作用、函数功能相关内容,阅读专题下面的文章了解更多详细教程。

19

2026.02.03

while的用法
while的用法

while的用法是“while 条件: 代码块”,条件是一个表达式,当条件为真时,执行代码块,然后再次判断条件是否为真,如果为真则继续执行代码块,直到条件为假为止。本专题为大家提供while相关的文章、下载、课程内容,供大家免费下载体验。

107

2023.09.25

scripterror怎么解决
scripterror怎么解决

scripterror的解决办法有检查语法、文件路径、检查网络连接、浏览器兼容性、使用try-catch语句、使用开发者工具进行调试、更新浏览器和JavaScript库或寻求专业帮助等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

492

2023.10.18

500error怎么解决
500error怎么解决

500error的解决办法有检查服务器日志、检查代码、检查服务器配置、更新软件版本、重新启动服务、调试代码和寻求帮助等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

382

2023.10.25

堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

443

2023.07.18

堆和栈区别
堆和栈区别

堆(Heap)和栈(Stack)是计算机中两种常见的内存分配机制。它们在内存管理的方式、分配方式以及使用场景上有很大的区别。本文将详细介绍堆和栈的特点、区别以及各自的使用场景。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

605

2023.08.10

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

765

2023.08.10

C# ASP.NET Core微服务架构与API网关实践
C# ASP.NET Core微服务架构与API网关实践

本专题围绕 C# 在现代后端架构中的微服务实践展开,系统讲解基于 ASP.NET Core 构建可扩展服务体系的核心方法。内容涵盖服务拆分策略、RESTful API 设计、服务间通信、API 网关统一入口管理以及服务治理机制。通过真实项目案例,帮助开发者掌握构建高可用微服务系统的关键技术,提高系统的可扩展性与维护效率。

76

2026.03.11

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
最新Python教程 从入门到精通
最新Python教程 从入门到精通

共4课时 | 22.5万人学习

Django 教程
Django 教程

共28课时 | 5万人学习

SciPy 教程
SciPy 教程

共10课时 | 1.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号