使用python socketserver如何将变量传递给处理程序类的构造函数

With python socketserver how can I pass a variable to the constructor of the handler class

我想将我的数据库连接传递给EchoHandler类,但我根本无法弄清楚如何执行此操作或访问EchoHandler类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class EchoHandler(SocketServer.StreamRequestHandler):
    def handle(self):
        print self.client_address, 'connected'

if __name__ == '__main__':
    conn = MySQLdb.connect (host ="10.0.0.5", user ="user", passwd ="pass", db ="database")

    SocketServer.ForkingTCPServer.allow_reuse_address = 1

    server = SocketServer.ForkingTCPServer(('10.0.0.6', 4242), EchoHandler)

    print"Server listening on localhost:4242..."
    try:
        server.allow_reuse_address
        server.serve_forever()
    except KeyboardInterrupt:
        print"
bailing..."

不幸的是,实际上没有一种简单的方法可以直接从服务器外部访问处理程序。

您有两种方法可以获取EchoHandler实例的信息:

  • 将连接存储为服务器的属性(在调用server_forever()之前添加server.conn = conn),然后通过self.server.conn访问EchoHandler.handler中的该属性。
  • 您可以覆盖服务器的finish_request并在那里分配值(您必须将它传递给EchoHandler的构造函数并覆盖EchoHandler .__ init__)。这是一个非常混乱的解决方案,它几乎要求您将连接存储在服务器上。
  • 我最喜欢的选择:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class EchoHandler(SocketServer.StreamRequestHandler):
        def handle(self):
            # I have no idea why you would print this but this is an example
            print( self.server.conn );
            print self.client_address, 'connected'

    if __name__ == '__main__':
        SocketServer.ForkingTCPServer.allow_reuse_address = 1

        server = SocketServer.ForkingTCPServer(('10.0.0.6', 4242), EchoHandler)
        server.conn = MySQLdb.connect (host ="10.0.0.5",
                         user ="user", passwd ="pass", db ="database")
        # continue as normal


    Mark T在python列表存档上有以下说法

    在处理程序类中,self.server引用服务器对象,因此是子类
    服务器和覆盖init以获取任何其他服务器参数
    并将它们存储为实例变量。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    import SocketServer

    class MyServer(SocketServer.ThreadingTCPServer):

        def __init__(self, server_address, RequestHandlerClass, arg1, arg2):
            SocketServer.ThreadingTCPServer.__init__(self,
                                                     server_address,
                                                     RequestHandlerClass)
            self.arg1 = arg1
            self.arg2 = arg2


    class MyHandler(SocketServer.StreamRequestHandler):

        def handle(self):
            print self.server.arg1
            print self.server.arg2


    我相信更多pythonic的另一种方法是执行以下操作:

    1
    2
    3
    4
    5
    6
    7
    8
    class EchoHandler(SocketServer.StreamRequestHandler):
      def __init__(self, a, b):
        self.a = a
        self.b = b

      def __call__(self, request, client_address, server):
        h = EchoHandler(self.a, self.b)
        SocketServer.StreamRequestHandler.__init__(h, request, client_address, server)

    您现在可以将处理程序的实例提供给TCPServer:

    1
    SocketServer.ForkingTCPServer(('10.0.0.6', 4242), EchoHandler("aaa","bbb"))

    TCPServer通常会为每个请求创建一个新的EchoHandler实例,但在这种情况下,将调用__call__方法而不是构造函数(它已经是一个实例。)

    在call方法中,我显式地创建了当前EchoHandler的副本,并将其传递给超级构造函数,以符合"每个请求一个处理程序实例"的原始逻辑。

    值得一看的是SocketServer模块,以了解这里发生的事情:https://github.com/python/cpython/blob/2.7/Lib/SocketServer.py


    我目前正在解决同样的问题,但我使用了稍微不同的解决方案,我觉得它更好一点,更一般(受@aramaki启发)。

    EchoHandler中,您只需要覆盖__init__并指定自定义Creator方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class EchoHandler(SocketServer.StreamRequestHandler):
        def __init__(self, request, client_address, server, a, b):
            self.a = a
            self.b = b
            # super().__init__() must be called at the end
            # because it's immediately calling handle method
            super().__init__(request, client_address, server)

        @classmethod
        def Creator(cls, *args, **kwargs):
            def _HandlerCreator(request, client_address, server):
                cls(request, client_address, server, *args, **kwargs)
            return _HandlerCreator

    然后你可以调用Creator方法并传递你需要的任何东西。

    1
    SocketServer.ForkingTCPServer(('10.0.0.6', 4242), EchoHandler.Creator(0,"foo"))

    主要的好处是,通过这种方式,您不会创建任何超出必要的实例,而是以更易于管理的方式扩展类 - 您无需再次更改Creator方法。