使用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...")
关键修改说明
修复HTTP响应格式:
- 补充了状态行和必要响应头,让curl能正确解析响应内容
- 用
\r\n完成协议要求的换行和分隔,这是HTTP协议的强制规则
修复Protocol实例问题:
- 移除了原有的
factory()方法和冗余的类属性 - 直接将
Srv类传入create_server,asyncio会自动为每个新连接生成独立的Srv实例,这是官方推荐的正确用法
- 移除了原有的
现在用curl测试就能正常收到响应了:
curl http://127.0.0.1:12345?foo=bar # 输出:Hi!
内容的提问来源于stack exchange,提问作者bipll
相关产品推荐
相关产品推荐

