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

Python非阻塞Socket实现P2P聊天单向通信问题求助

排查Python简易P2P聊天程序单向通信问题

我尝试用Python实现一个简易的点对点(P2P)聊天程序,第一个用户运行下方的server.py程序绑定Socket,另一用户可通过netcat连接进行通信。但目前程序仅能获取本地输入并发送给对方,无法接收对方发送的消息,相关代码如下:

import sys
import socket
import select
import threading

# Bind to socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('127.0.0.1', 11111))
s.listen()

def chat(conn, addr):

    # Set blocking to false so that program can send and receive messages at the same time
    conn.setblocking(0)

    # Receive messages using select
    while conn in select.select([conn], [], [], 0)[0]:
        text = conn.recv(4096)
        if text:
            print("{}: {}".format(addr, text))
        else:
            return
    
    # get user input and send message
    while True:
        msg = input(">>>")
        conn.send(msg.encode())
            

if __name__ == '__main__':
    
    ## Accept connections and start new thread
    (conn, addr) = s.accept() 
    threading.Thread(target=chat, args=([conn, addr])).start()

问题原因

  • 接收逻辑仅执行一次:接收消息的while循环使用了超时时间为0的select(非阻塞检查),如果此时没有消息到达,会直接跳出接收循环,进入永久的输入发送循环,再也不会回头检查新消息。
  • 收发逻辑串行阻塞:整个chat函数里,接收和发送是串行执行的,没有同时监听输入和Socket消息的机制,导致程序只能专注于发送操作,无法处理后续的接收请求。

解决方法

需要让程序同时监听Socket连接的可读事件和标准输入的可读事件,实现收发并行。以下提供两种可行方案:

方案1:单线程用select同时监听Socket和标准输入

import sys
import socket
import select

# Bind to socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('127.0.0.1', 11111))
s.listen()

def chat(conn, addr):
    # 同时监听Socket连接和标准输入
    inputs = [conn, sys.stdin]
    while True:
        readable, _, _ = select.select(inputs, [], [])
        for sock in readable:
            if sock == conn:
                # 处理对方发送的消息
                text = sock.recv(4096)
                if text:
                    # 用\r和end=""修正打印格式,避免打断输入提示符
                    print(f"\r{addr}: {text.decode()}\n>>>", end="")
                else:
                    print("\n对方已断开连接")
                    return
            elif sock == sys.stdin:
                # 处理本地输入并发送
                msg = input(">>>")
                conn.send(msg.encode())

if __name__ == '__main__':
    conn, addr = s.accept() 
    chat(conn, addr)

方案2:双线程分别处理接收和发送

import sys
import socket
import threading

# Bind to socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('127.0.0.1', 11111))
s.listen()

def receive_messages(conn, addr):
    while True:
        try:
            text = conn.recv(4096)
            if text:
                print(f"\r{addr}: {text.decode()}\n>>>", end="")
            else:
                print("\n对方已断开连接")
                break
        except Exception as e:
            print(f"\n接收消息出错: {e}")
            break

def send_messages(conn):
    while True:
        try:
            msg = input(">>>")
            conn.send(msg.encode())
        except Exception as e:
            print(f"\n发送消息出错: {e}")
            break

if __name__ == '__main__':
    conn, addr = s.accept() 
    # 启动接收线程,设为守护线程随主线程退出
    threading.Thread(target=receive_messages, args=(conn, addr), daemon=True).start()
    # 主线程处理发送逻辑
    send_messages(conn)

关键改动说明

  • 方案1通过select.select同时监控Socket和标准输入,只要其中一个有可读事件就触发处理,实现了收发并行。
  • 方案2拆分出独立的接收线程专门监听对方消息,主线程负责输入发送,两个线程并行运行,避免了串行阻塞。
  • 加入\r和end=""修正打印格式,防止对方消息打断输入提示符的正常显示。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 07:15:50