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

使用asyncio编写TCP服务器时`asyncio.Transport.write()`无效果问题

问题分析与解决方案

核心问题1:返回内容不符合HTTP协议规范

curl作为HTTP客户端,要求接收标准格式的HTTP响应,但你目前只返回了纯字节串b'Hi!',这完全不符合HTTP协议的要求——客户端无法识别这个内容,因此判定为“空回复”。

一个最简化的合法HTTP响应必须包含:

  • 状态行:比如HTTP/1.1 200 OK\r\n,告知客户端请求处理结果
  • 响应头:至少需要Content-Length(说明正文字节数)、Content-Type(指定内容类型),每个头格式为Key: Value\r\n
  • 空行:\r\n,用于分隔响应头和正文
  • 正文内容

核心问题2:Protocol实例复用错误

你的factory()方法返回的是同一个Srv实例,这违反了asyncio的工作机制——每个TCP连接都需要对应一个独立的Protocol实例,复用实例会导致连接处理逻辑混乱。

修改后的完整代码

import asyncio
import socket
import urllib.parse
from http.server import BaseHTTPRequestHandler
from io import BytesIO

class HTTPRequest(BaseHTTPRequestHandler):
    def __init__(self, request_text):
        self.rfile = BytesIO(request_text)
        self.raw_requestline = self.rfile.readline()
        self.error_code = self.error_message = None
        self.parse_request()
        rpc_request = urllib.parse.urlparse(self.path)
        self.endpoint = rpc_request.path
        self.query = urllib.parse.parse_qs(rpc_request.query)
    def send_error(self, code, message):
        self.error_code = code
        self.error_message = message

class Srv(asyncio.Protocol):
    def connection_made(self, transport):
        self._transport = transport
    def data_received(self, data):
        request = HTTPRequest(data)
        print("\tQuery:", request.query)
        
        # 构造符合HTTP规范的响应
        response_body = b'Hi!'
        response = (
            b'HTTP/1.1 200 OK\r\n'
            b'Content-Type: text/plain\r\n'
            f'Content-Length: {len(response_body)}\r\n'.encode()
            b'\r\n'  # 空行分隔响应头与正文
            + response_body
        )
        self._transport.write(response)
        # 写完响应后关闭连接
        self._transport.close()
    def eof_received(self):
        pass

async def run_server(address=('127.0.0.1', 12345), family=socket.AF_INET):
    loop = asyncio.get_event_loop()
    # 直接传入Srv类作为工厂,asyncio会自动为每个连接创建新实例
    if family == socket.AF_UNIX:
        server = await loop.create_unix_server(Srv, address=address)
    else:
        server = await loop.create_server(Srv, host=address[0], port=address[1], family=family)
    
    await server.wait_closed()

if __name__ == "__main__":
    try:
        asyncio.get_event_loop().run_until_complete(run_server())
    except KeyboardInterrupt:
        print("Server shutting down...")

关键修改说明

  1. 修复HTTP响应格式:

    • 补充了状态行和必要响应头,让curl能正确解析响应内容
    • 用\r\n完成协议要求的换行和分隔,这是HTTP协议的强制规则
  2. 修复Protocol实例问题:

    • 移除了原有的factory()方法和冗余的类属性
    • 直接将Srv类传入create_server,asyncio会自动为每个新连接生成独立的Srv实例,这是官方推荐的正确用法

现在用curl测试就能正常收到响应了:

curl http://127.0.0.1:12345?foo=bar
# 输出:Hi!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 07:39:09