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

Python Socket编程:使用多线程模拟支持多客户端的广播流服务

问题根因

你当前的实现核心问题在于预创建了10个固定队列,生产者线程会向所有队列写入数据,只要存在未被消费的队列,队列满后put()方法就会进入永久阻塞,直接导致生产者线程挂起,所有客户端都会出现断流。另外你现有代码还存在函数名笔误:生产者函数定义为readTheMusics,但主函数创建线程时指定的target是musicReading,运行会直接报错。

优化方案

方案1:动态管理活跃客户端队列(更推荐)

无需预先创建固定数量的队列,客户端连接时动态生成专属队列,断开后自动移除,生产者仅向当前活跃的队列写入数据,从根源上避免空闲队列阻塞的问题。
修改后参考代码:

import wave
import threading
from queue import Queue
import socket

# 配置项自行补充
CHUNK = 1024
serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 省略TCP绑定、监听配置

# 线程安全的活跃队列列表,加锁避免遍历和修改冲突
active_queues = []
queue_lock = threading.Lock()

# 生产者线程
def readTheMusics():
    i = 1
    while True:
        fname = f"music{i}.wav"
        wf = wave.open(fname, 'rb')
        data = wf.readframes(CHUNK)
        while data:
            # 加锁遍历队列,避免遍历过程中队列列表被修改
            with queue_lock:
                for q in active_queues:
                    # 队列满时先丢旧帧再放新帧,不阻塞
                    if q.full():
                        try:
                            q.get(block=False)
                        except:
                            pass
                    q.put(data, block=False)
            data = wf.readframes(CHUNK)
        wf.close()
        i = 2 if i == 1 else 1

# 消费者线程
def connection(connectionSocket, addr):
    # 为当前客户端创建专属队列
    client_queue = Queue(2)
    with queue_lock:
        active_queues.append(client_queue)
    try:
        while True:
            data = client_queue.get(block=True)
            connectionSocket.sendall(data)
    except:
        # 客户端断开连接,清理队列
        with queue_lock:
            if client_queue in active_queues:
                active_queues.remove(client_queue)
        connectionSocket.close()

def main():
    th2 = threading.Thread(target=readTheMusics, daemon=True)
    th2.start()
    while True:
        connectionSocket, addr = serverSocket.accept()
        th = threading.Thread(target=connection, args=(connectionSocket, addr), daemon=True)
        th.start()
                    
if __name__ == '__main__':
    main()

方案2:兼容原有固定队列逻辑的修改

如果要保留10个固定队列的设计,只要修改写入队列的逻辑,队列满时自动丢弃旧帧即可,不会阻塞生产者:
将生产者中写入队列的部分改为:

for k in range(10):
    if queue[k].full():
        try:
            queue[k].get(block=False)
        except:
            pass
    queue[k].put(data, block=False)
注意事项
  • 队列大小建议设置为2~3,既可以缓冲少量网络波动避免客户端丢帧,也不会出现内存泄漏问题
  • 所有线程建议设置为daemon=True,主程序退出时线程会自动销毁,不会出现残留进程
  • 要捕获socket发送数据的异常,客户端断开后及时清理对应的队列,避免无效写入占用资源

内容的提问来源于stack exchange,提问作者gtsicpd

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.07 12:36:02