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

26

我想把我的数据库连接传递给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 "服务器正在监听 localhost:4242..." try: server.allow_reuse_address server.serve_forever() except KeyboardInterrupt: print "\n退出..."
5个回答

33

很抱歉,从服务器外部没有一种简单的方式可以直接访问处理程序。

你有两个选项来将信息传递给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

11

Mark 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


为什么在MyServer.__init__中不使用super().__init__(...)呢? - Johannes Bittner
2
SocketServer模块中的类是“旧式”类。必须以这种方式调用super构造函数。请参见此处:https://dev59.com/DWgu5IYBdhLWcg3wK0Gr - ant1g
@ShivangiSingh 最好发一个问题,这样我们就可以看到你的代码。 - demented hedgehog

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

1

我认为更符合Python风格的另一种方法是这样做:

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__方法而不是构造函数(它已经是一个实例)。
在call方法中,我明确复制了当前的EchoHandler,并将其传递给超级构造函数,以符合“每个请求一个处理程序实例”的原始逻辑。
值得一看的是SocketServer模块,以了解这里发生了什么:https://github.com/python/cpython/blob/2.7/Lib/SocketServer.py

1
我最近在解决同样的问题,但是我使用了稍微不同的方法,我觉得它更加优雅和通用(受 @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方法。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接