【问题标题】:Python selector.select returning empty socket as readablePython selector.select 返回空套接字为可读
【发布时间】:2018-08-28 16:48:34
【问题描述】:

我在 Windows 10 Pro x64 上的 Python 3.6 中实现基本 HTTPServer 时遇到问题。

本质上,socketserver.serve_forever() 的基本实现中使用的选择器使用选择器来确定套接字是否可读。问题是在服务器接收到单个 GET 请求后,它总是评估为真。因此,让我们以接收到单个 GET 请求的场景为例。 GET 被处理,套接字被刷新,但selector.select 仍然返回 true,因此服务器尝试读取空套接字,导致它挂起并阻塞。

socketserver.serveforever:

def serve_forever(self, poll_interval=0.5):
        """Handle one request at a time until shutdown.

        Polls for shutdown every poll_interval seconds. Ignores
        self.timeout. If you need to do periodic tasks, do them in
        another thread.
        """
        self.__is_shut_down.clear()
        try:
            # XXX: Consider using another file descriptor or connecting to the
            # socket to wake this up instead of polling. Polling reduces our
            # responsiveness to a shutdown request and wastes cpu at all other
            # times.
            with _ServerSelector() as selector:
                selector.register(self, selectors.EVENT_READ)  
                while not self.__shutdown_request:
                    ready = selector.select(poll_interval)
                    if ready:
                        self._handle_request_noblock()

                    self.service_actions()
        finally:
            self.__shutdown_request = False
            self.__is_shut_down.set()

selector.select:

    if sys.platform == 'win32':
        def _select(self, r, w, _, timeout=None):
            r, w, x = select.select(r, w, w, timeout)
            return r, w + x, []
    else:
        _select = select.select

    def select(self, timeout=None):
        timeout = None if timeout is None else max(timeout, 0)
        ready = []
        try:
            r, w, _ = self._select(self._readers, self._writers, [], timeout)
        except InterruptedError:
            return ready
        r = set(r)
        w = set(w)
        for fd in r | w:
            events = 0
            if fd in r:
                events |= EVENT_READ
            if fd in w:
                events |= EVENT_WRITE

            key = self._key_from_fd(fd)
            if key:
                ready.append((key, events & key.events))
        return ready

这似乎是问题所在selector 中的行。一旦收到一次 GET 请求,它就会永久返回 r 的值:

r, w, x = select.select(r, w, w, timeout).

** EDIT1 一个失败的基本暗示

MyServer.py

import http
from http import server

class Server(server.HTTPServer):
    pass

MyRequestHandler.py

from http.server import BaseHTTPRequestHandler
import urllib

class OAuthGrantRequestHandler(BaseHTTPRequestHandler):
    """docstring"""

    def do_GET(self):
        self.send_response(200)
        self.send_header('Content-type', 'text/html')
        self.end_headers()
        parts = urllib.parse.urlparse(self.path)
        self.wfile.write(
            b'<html><head><title>Authentication Status</title></head>'
            b'<body><p>The authentication flow has completed.</p>')

        print("Request handler completed")
        return

MyTest.py

import MyServer
import MyRequestHandler
import threading

def AwaitCallback(server_class=MyServer.Server,
                    handler_class=MyRequestHandler.OAuthGrantRequestHandler):
    """docstring"""

    server_address = ("127.0.0.1", 8080)
    Httpd = server_class(server_address, handler_class)
    Httpd.timeout = 200
    t1 = threading.Thread(target=Httpd.serve_forever, args=(1,))

    try:
        t1.start()

    finally:

        if t1:
            t1.join()
        print("thread 3 terminated")
        if Httpd:
            Httpd.server_close()

    return 

AwaitCallback()

运行我的测试后,我只是将 localhost:8080 放到我最喜欢的浏览器和 wallah 中,服务器成功响应然后锁定。

【问题讨论】:

  • 这个问题只在windows上吗?即您是否测试过代码在 Linux 上是否按预期工作,或者您不知道?
  • 在另一个帮助调试的注释中,你能在select返回后立即print (r)吗?
  • @GhasemNaddaf,不知道。我现在没有要开发的 Linux 机器。我确实相信这是一个 Windows 问题,因为 select.select 方法文档说它调用了操作系统的 select 方法。
  • 我可以看到输出,我已经设置了监听器。 select.select 正在返回看起来像 r 中的文件描述符。
  • 所以,我将您的代码与此处给出的 select 示例进行比较 steelkiwi.com/blog/working-tcp-sockets 。您实际在哪里读取数据,即r.recv()

标签: python python-3.x selector httpserver


【解决方案1】:

在 selector.select 上 改变

def _select(self, r, w, _, timeout=None):

进入

def _select(self, r, w, x, timeout=None):

【讨论】:

    【解决方案2】:

    以防万一其他人遇到这个问题并遇到与HTTPServer 相同的问题,让我们提一下 Python 标准库自 3.7 版以来的新更新:

    类 http.server.ThreadingHTTPServer(server_address, RequestHandlerClass)

    此类与 HTTPServer 相同,但使用线程通过 ThreadingMixIn 处理请求。这对于处理 Web 浏览器预打开套接字很有用,HTTPServer 会无限期地等待。

    3.7 版中的新功能。

    【讨论】:

      猜你喜欢
      • 2016-04-26
      • 2018-11-19
      • 2023-02-10
      • 1970-01-01
      • 1970-01-01
      • 2013-11-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多