【问题标题】:Can't see any effect of `asyncio.Transport.write()`看不到 `asyncio.Transport.write()` 的任何效果
【发布时间】:2020-03-30 11:51:15
【问题描述】:

我正在尝试使用 asyncio 制作一个简单的自制 localhost TCP 服务器(它只有 Python 3.6.9,因为我在这台机器上只有一个 LTS Ubuntu):

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 __init__(self, address=('127.0.0.1', 12345), family=socket.AF_INET, loop: asyncio.AbstractEventLoop=None):
        self._address = address
        self._family = family
        self._loop = loop if loop is not None else asyncio.get_event_loop()
        self._server = None

    def connection_made(self, transport):
        self._transport = transport

    def data_received(self, data):
        request = HTTPRequest(data)
        print("\tQuery:", request.query)
        self._transport.write(b'Hi!')
        self._transport.close()

    def eof_received(self):
        pass

    def factory(self):
        return self

    async def run(self):
        sock = socket.socket(family=self._family)
        sock.bind(self._address)
        sock.listen()
        if self._family == socket.AF_UNIX:
            self._server = await self._loop.create_unix_server(protocol_factory=self.factory, sock=sock)
        else:
            self._server = await self._loop.create_server(protocol_factory=self.factory, sock=sock, family=self._family)
        self._running = True

omg = Srv()

async def wat():
    await omg.run()
    await omg._server.wait_closed()

try:
    asyncio.get_event_loop().run_until_complete(wat())
except:
    omg._server.close()

好像启动,甚至接收数据:

$ python3 0_o.py 
    Query: {'data': ['"abcd"']}
    Query: {'data': ['"abcd"']}

(打印来自Srv.data_received),但 curl 说它没有收到任何回复:

$ curl '127.0.0.1:12345/query?data="abcd"'
curl: (52) Empty reply from server
$ curl '127.0.0.1:12345/query?data="abcd"'
curl: (52) Empty reply from server

从我的角度来看,我已经重新实现了 python 的 stdlib 文档中的 echo 服务器示例中的所有内容,但显然 data_received() 中的 write()close() 似乎没有产生任何效果。

我错过了什么?

【问题讨论】:

    标签: python-3.6 python-asyncio


    【解决方案1】:

    据我所知,您的代码的唯一问题是您没有发出正确的 HTTP 响应,而只是发出了字符串“Hi”。例如,不更改地运行代码并将其连接到nc 会导致以下结果:

    $ nc localhost 12345
    GET / HTTP/1.0
    Hi!
    

    注意不仅没有正确的响应,而且服务器甚至没有等待客户端完成请求。相反,它立即打印了一个格式错误的响应。

    curl 连接会产生以下输出:

    $ curl http://localhost:12345/
    curl: (1) Received HTTP/0.9 when not allowed
    

    “HTTP/0.9”指的是 HTTP 协议的古老版本,其中状态行是可选的。此响应被 curl 拒绝,但 Wget 会接受它:

    $ wget -qO- http://localhost:12345/
    Hi!
    

    您可以查看为您执行此操作的库,例如 aiohttp,而不是尝试手动实现 HTTP。

    【讨论】:

    • 我已将回复更改为self._transport.writelines([b'HTTP/1.1 200 OK', b'Content-Type: application/json', b'Content-Length: 15', b'{"hi": "there"}']),但仍然没有任何内容。 ://
    • @bipll 您没有包含换行符。请不要尝试自己重新实现 HTTP,使用 aiohttp 或等效库。
    • 好的,换行符似乎有帮助,谢谢。我没有重新实现 HTTP,我正在尝试从我的脚本中压缩任何输出。
    • @bipll 手动打印 HTTP 标头正在重新实现 HTTP,但如果它适合您...
    猜你喜欢
    • 2021-09-08
    • 2019-01-24
    • 1970-01-01
    • 2012-05-04
    • 2013-03-13
    • 2019-04-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多