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

如何用asyncio正确编写自定义Socket服务器?Python3迁移求助

Python3 asyncio Socket服务器无响应冻结问题

原Python2实现代码

我之前在Python2中使用asyncore.dispatcher实现了如下Socket服务器:

import asyncore
import socket

class Server(asyncore.dispatcher):

    def __init__(self, host, port, socketMap=None):
        asyncore.dispatcher.__init__(self, None, socketMap)
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
        self.set_reuse_addr()
        self.bind((host, port))
        self.listen(5)
        
    def handle_accept(self):
        sock, addr = self.accept()
        handler = Handler(self, sock, self._map)

    def run(self):
        while True:
            asyncore.loop(0.001, True, self._map, 1)
        self.handle_close()

class Handler(asyncore.dispatcher):
    def __init__(self, server, sock, socketMap):
        self._server = server
        asyncore.dispatcher.__init__(self, sock, socketMap)

    def handle_read(self):
        buf = self.recv(8192)
        print(buf)
        
    def handle_write(self): 
        sent = self.send("hello")

问题描述

我在Python3中用asyncio重写了代码,但客户端发送数据后服务器无响应且直接冻结,重写的代码如下:

import socket
import asyncio

class MyAsyncioHandler:

    def __init__(self, reader, writer):
        self.reader = reader
        self.writer = writer

    async def handle_read(self):
        data = await self.reader.read(8192)
        if data:
            print(f"Received data: {data.decode()}")

    async def handle_write(self, data):
        self.writer.write(data)
        await self.writer.drain()

    def handle_close(self):
        print("Connection closed")
        self.writer.close()

async def handle_client(reader, writer):
    handler = MyAsyncioHandler(reader, writer)
    while True:
        await handler.handle_read()
        data_to_send = b"Response data"
        await handler.handle_write(data_to_send)

async def main():
    server = await asyncio.start_server(
        handle_client, '127.0.0.1', 5000    )
    addr = server.sockets[0].getsockname()
    print(f"Serving on {addr}")
    async with server:
        await server.serve_forever()
        
if __name__ == '__main__':
    asyncio.run(main())

问题原因与修复方案

问题根源

  1. 无限等待读取:reader.read(8192)会一直阻塞等待新数据,若客户端发送一次数据后不再发送,服务器会卡在这一步,表现为"冻结"。
  2. 未处理连接关闭:当客户端主动关闭连接时,reader.read()会返回空字节,但原代码没有判断该情况,导致循环无限执行无效的读取操作。

修复后的代码

import asyncio

class MyAsyncioHandler:

    def __init__(self, reader, writer):
        self.reader = reader
        self.writer = writer

    async def handle_read(self):
        data = await self.reader.read(8192)
        if not data:
            return None  # 返回None标记连接关闭
        print(f"Received data: {data.decode()}")
        return data

    async def handle_write(self, data):
        self.writer.write(data)
        await self.writer.drain()

    async def handle_close(self):
        print("Connection closed")
        self.writer.close()
        await self.writer.wait_closed()  # 确保连接完全关闭

async def handle_client(reader, writer):
    handler = MyAsyncioHandler(reader, writer)
    try:
        while True:
            data = await handler.handle_read()
            if data is None:
                break  # 连接关闭,退出循环
            # 与原Python2代码保持一致,发送"hello"响应
            await handler.handle_write(b"hello")
    finally:
        await handler.handle_close()

async def main():
    server = await asyncio.start_server(
        handle_client, '127.0.0.1', 5000
    )
    addr = server.sockets[0].getsockname()
    print(f"Serving on {addr}")
    async with server:
        await server.serve_forever()

if __name__ == '__main__':
    asyncio.run(main())

修复说明

  • 在handle_read中添加空数据判断,返回None标记连接关闭,让循环能正常退出。
  • 使用try/finally块确保无论连接正常关闭还是异常终止,都能正确释放连接资源。
  • 将响应内容改为b"hello",和原Python2代码的逻辑保持一致。
  • 新增await writer.wait_closed(),确保连接彻底关闭,避免资源泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 10:17:35