Python中socket server服务端如何主动向客户端发送消息?

我查看了这里的资料, 看到 server 发送消息都是在 handle 里面也就是说要先有客户端有消息发送过来才会做出对应的发送消息的动作, 那么如果我想在服务端主动的发送消息给客户端这个应该怎么做? https://docs.python.org/3/library/socketserver.html#socketserver.BaseServer.server_bind

这个是实例代码, 这个 handle 里面处理了消息发送的过程。

import socketserver

class MyUDPHandler(socketserver.BaseRequestHandler): “”" This class works similar to the TCP handler class, except that self.request consists of a pair of data and client socket, and since there is no connection the client address must be given explicitly when sending data back via sendto(). “”"

def handle(self):
    data = self.request[0].strip()
    socket = self.request[1]
    print("{} wrote:".format(self.client_address[0]))
    print(data)
    socket.sendto(data.upper(), self.client_address)

if name == “main”: HOST, PORT = “localhost”, 9999 with socketserver.UDPServer((HOST, PORT), MyUDPHandler) as server: server.serve_forever()


Python中socket server服务端如何主动向客户端发送消息?

4 回复

建议你看看 TCP
如果非要用 UDP 的话,你就可以在第一次收到这个 client address 的时候把这个 socket 和 address 存起来
你想在哪里主动发数据的话就再用前面记录下来的 socket 和 address

如果你是想在 client 没有发包过来的时候就发包给他
那么你把这个 client 实现成你代码里面的 UDPServer 就好了,这样你就能提前这个 client address


在Python的socket编程里,服务端想主动给客户端发消息,关键在于保存好客户端的socket对象

当服务端通过accept()接受一个客户端连接后,会返回一个专门用于和这个客户端通信的新socket对象(conn)以及客户端地址。你只要在后续的代码里持有这个conn对象,随时都可以调用它的send()sendall()方法。

下面是一个简单的多线程TCP服务端示例,它会在接受连接后,主动向客户端发送一条欢迎消息,然后每隔5秒发送一次当前时间:

import socket
import threading
import time
from datetime import datetime

def handle_client(conn, addr):
    """处理单个客户端连接的线程函数"""
    print(f"[+] 新连接来自: {addr}")
    
    try:
        # 1. 连接建立后,立即主动发送一条消息
        welcome_msg = f"欢迎连接到服务器,你的地址是 {addr}\n"
        conn.sendall(welcome_msg.encode('utf-8'))
        
        # 2. 循环,定期主动发送消息
        while True:
            time.sleep(5)  # 等待5秒
            current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            msg = f"服务器时间: {current_time}\n"
            conn.sendall(msg.encode('utf-8'))
            print(f"[>] 已主动向 {addr} 发送: {msg.strip()}")
            
    except (ConnectionResetError, BrokenPipeError):
        print(f"[-] 客户端 {addr} 断开了连接")
    finally:
        conn.close()

def start_server(host='127.0.0.1', port=65432):
    """启动服务器"""
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server_socket.bind((host, port))
    server_socket.listen()
    print(f"[*] 服务器监听在 {host}:{port}")
    
    try:
        while True:
            conn, addr = server_socket.accept()  # 等待客户端连接
            # 为每个新客户端创建一个线程来处理
            client_thread = threading.Thread(target=handle_client, args=(conn, addr))
            client_thread.daemon = True
            client_thread.start()
    except KeyboardInterrupt:
        print("\n[*] 服务器关闭")
    finally:
        server_socket.close()

if __name__ == "__main__":
    start_server()

核心要点:

  • conn, addr = server_socket.accept():这行代码返回的连接对象conn就是服务端与特定客户端通信的通道。
  • conn.sendall(data):使用这个连接对象的方法,就可以随时向对应的客户端发送数据。数据需要先编码成字节(例如.encode('utf-8'))。
  • 多线程/异步:为了能同时处理多个客户端并实现“主动”发送,通常需要为每个客户端连接创建独立的线程(如上例)、进程或使用asyncio等异步框架。否则,单线程的服务端在recv()等待客户端消息时会被阻塞。

简单来说,服务端主动发消息就是:在连接有效期内,用accept()得到的那个socket对象直接调用send()

这是一个很常见的问题,百度关键字 udp 打洞。
至于怎么做你自己百度吧。

socketserver 里对于 TCP 协议,所谓“消息”是指一个连接
对于 UDP 协议,当然必须先收到消息才能确定角色。如果没收到就发,那叫客户端不叫服务器

回到顶部