python 中具有活动 TCP 连接的 HTTP 服务器

HTTP Server with active TCP connections in python

我正在 python 中编写一个伪 http 应用程序,其要求是:

  1. 它应该处理 HTTP 请求。
  2. 客户端和服务器之间的连接比请求-响应还长,即在向客户端发送响应后,底层 TCP 连接仍然有效。
  3. 服务器需要能够将数据发送到它已经打开连接的特定客户端。

我看过 twisted 和 python 的 TCPServer/BaseHTTPServer,但它们不太符合要求。在我看来,我有两个选择:

  1. 从 HTTP 服务器实现开始,覆盖我的连接管理方式。
  2. 有一个简单的套接字服务器,它将管理连接并在 "http" 服务器和客户端之间传递数据。

有没有人解决过类似的问题?关于其他方法的任何想法或哪一个是更好的选择?

谢谢!

编辑 1 我不能使用 HTTP 2 或网络套接字; HTTP <2 over TCP 是硬性要求。

由于您不能使用 websockets 或 http/2,并且您需要能够将数据从服务器推送到客户端,那么长轮询可能是剩下的最佳选择。

参见 https://github.com/twisted/nevow 上的 Nevow,了解通过 athena 模块实现长轮询的一种可能方式。

我最终覆盖了 http.server.HTTPServer 中的方法,它比预期的要少,而且全部来自标准包。

根据您的情况,以下内容最终可能会涉及更多,例如使用更结构化的 session 表示等。在这种情况下,您可能应该再次考虑更发达的框架,例如 twisted

要点是:

  • 使用 ThreadingMixIn - 因为连接是 long-lived,所以需要一个单独的处理程序线程以便一次处理多个连接。
  • 请注意,如果您使用 BaseHTTPRequestHandler,则每次响应后连接都会关闭,除非有 Connection: keep-alive header 或您在 EVERY 上设置了 self.close_connection = False请求。

无论如何,让您入门的片段:

from http.server import HTTPServer, BaseHTTPRequestHandler
from socketserver import ThreadingMixIn

class MyHandler(BaseHTTPRequestHandler):

   # Implement do_GET, do_POST, etc.

   def handle_one_request(self):
      super(MyHandler, self).handle_one_request()
      self.close_connection = some_condition()
      if self.close_connection:
         # Remove the session from the server as it will be closed after this
         # method returns
         self.server.sessions.pop(self.client_address)

class MyServer(ThreadingMixIn, HTTPServer):
   def __init__(self, addr_port, handler_class):
      super(MyServer, self).__init__(addr_port, handler_class)
      self.sessions = {} # e.g. (addr, port) -> client socket

   def get_request(self):
      """Just call the super's method and cache the client socket"""
      client_socket, client_addr = super(MyServer, self).get_request()
      self.sessions[client_addr] = client_socket
      return (client_socket, client_addr)

   # You may also want to add the following
   def server_close(self):
      """Close any leftover connections."""
      super(MyServer, self).server_close()
      for _, sock in self.sessions.items():
         try:
            sock.shutdown(socket.SHUT_WR)
         except socket.error:
            pass
         sock.close()