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
相关产品推荐
相关产品推荐

