如何将Linux进程stdout重定向至多个TCP连接?
问题分析与解决方案
核心问题
你需要将一个进程的stdout输出完整转发给所有接入指定TCP端口的连接,但测试socat时发现数据被分流到不同连接,每个连接只能拿到部分内容——这是因为socat的fork模式是将输入流分流给子进程,而非复制流给所有连接,不符合你的需求。
方案一:用现有命令行工具组合实现
通过命名管道+tee+socat的组合可以实现流复制,步骤如下:
- 创建临时目录存放命名管道:
mkdir -p /tmp/tcp-broadcast
- 启动
socat监听端口,为每个新连接创建独立的命名管道:
socat TCP-LISTEN:8080,reuseaddr,fork EXEC:'mkfifo /tmp/tcp-broadcast/fifo-$$; cat /tmp/tcp-broadcast/fifo-$$; rm /tmp/tcp-broadcast/fifo-$$'
- 将工作进程输出通过
tee复制到所有存在的命名管道:
# 替换成你的实际工作进程命令 for i in {0..10000}; do echo "round ${i}"; sleep 1; done | tee /tmp/tcp-broadcast/fifo-*
注意事项
- 若在
tee启动后有新连接接入,需重启tee才能让新连接的管道被包含进去,因为tee启动时只会读取当时存在的管道列表。
方案二:自制Python转发脚本
如果需要支持动态添加连接(后续接入的连接也能接收之后的完整输出),用Python脚本更灵活,以下是实现代码:
import socket import threading import sys LISTEN_PORT = 8080 client_connections = [] conn_lock = threading.Lock() def broadcast_to_clients(data): """将数据广播给所有活跃客户端""" with conn_lock: # 遍历副本避免修改列表时出错 for conn in client_connections[:]: try: conn.sendall(data) except Exception: # 连接失效,移除并关闭 client_connections.remove(conn) conn.close() def handle_client(conn): """保持连接,检测客户端是否在线""" try: # 空循环检测连接状态,收到空数据表示客户端断开 while conn.recv(1024): pass finally: with conn_lock: if conn in client_connections: client_connections.remove(conn) conn.close() def read_stdout(): """读取标准输入并触发广播""" while True: line = sys.stdin.readline() if not line: break broadcast_to_clients(line.encode('utf-8')) def main(): server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server_sock.bind(('0.0.0.0', LISTEN_PORT)) server_sock.listen(5) # 启动stdout读取线程 threading.Thread(target=read_stdout, daemon=True).start() print(f"Listening on port {LISTEN_PORT}...") while True: conn, addr = server_sock.accept() print(f"New connection from {addr}") with conn_lock: client_connections.append(conn) threading.Thread(target=handle_client, args=(conn,), daemon=True).start() if __name__ == "__main__": main()
使用方法
- 保存脚本为
stdout-broadcast.py - 运行工作进程并将输出管道到脚本:
# 替换成你的实际工作进程命令 for i in {0..10000}; do echo "round ${i}"; sleep 1; done | python stdout-broadcast.py
- 用
nc localhost 8080连接,所有连接都会收到完整输出,后续接入的连接也能获取之后的所有内容。
总结
- 无需动态添加连接时,用命名管道+tee+socat的组合可以快速实现,无需编写代码。
- 需要支持动态连接时,自制Python脚本是更可靠的选择。
内容的提问来源于stack exchange,提问作者Olli
相关产品推荐
相关产品推荐

