繁体   English   中英

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

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

我想将我的数据库连接传递给 EchoHandler 类,但是我根本不知道如何做到这一点或访问 EchoHandler 类。

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 "\nbailing..."

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

您有两种选择来获取 EchoHandler 实例的信息:

  1. 将连接存储为服务器的属性(在调用server_forever()之前添加server.conn = conn ),然后通过self.server.conn在 EchoHandler.handler 中访问该属性。
  2. 您可以覆盖服务器的finish_request并在那里分配值(您必须将其传递给 EchoHandler 的构造函数并覆盖 EchoHandler.__init__)。 这是一个更加混乱的解决方案,它几乎需要您无论如何都将连接存储在服务器上。

我的最佳选择:

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

马克T在python列表存档上有以下要说的

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


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,是执行以下操作:

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:

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

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

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

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

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

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

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方法并传递任何你需要的东西。

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

主要好处是,通过这种方式,您不会创建多余的实例,并且以更易于管理的方式扩展类 - 您不需要再次更改Creator方法。

似乎您不能使用ForkingServer来共享变量,因为当进程尝试修改共享变量时会发生写时复制。 将其更改为ThreadingServer ,您将能够共享全局变量。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM