如何将参数传递给 TCPServer 处理程序类

问题描述 投票:0回答:2

这是一段实验/教育代码,专门为了“玩”socketserver 功能而编写。

代码运行没有错误。它所做的只是打开并读取文件,通过 TCP 连接将文件内容发送到 TCP 服务器。服务器在重写 BaseRequestHandlerhandle() 函数期间打印接收到的数据。

from socket import socket, AF_INET, SOCK_STREAM
from socketserver import BaseRequestHandler, TCPServer
from threading import Thread

HOST = "localhost"
PORT = 10101
ADDR = HOST, PORT
RECVBUF = 4096
SENDBUF = RECVBUF // 2
INPFILE = "inputfile.txt"


class MyHandler(BaseRequestHandler):
    def handle(self):
        while data := self.request.recv(RECVBUF):
            print(data.decode(), end="")


def server(tcpserver: TCPServer):
    tcpserver.serve_forever()


if __name__ == "__main__":
    with open(INPFILE, "rb") as indata:
        with TCPServer(ADDR, MyHandler) as tcpserver:
            (t := Thread(target=server, args=[tcpserver])).start()
            with socket(AF_INET, SOCK_STREAM) as s:
                s.connect(ADDR)
                while chunk := indata.read(SENDBUF):
                    s.sendall(chunk)
            tcpserver.shutdown()
            t.join()

所以这很好,但是如果我想要 handle() 函数将接收到的数据写入文件怎么办?当然,我可以将文件名硬编码到 handle() 函数中,甚至可以使其全局可用。对我来说,这两个选项似乎都不是特别Pythonic。

TCPServer 是基于 RequestHandler 类型构造的 - 即,不是类实例。我真正想要做的是(以某种方式)将文件名传递到 MyHandler 类中。 TCPServer 实例(大概)将有一个用于构造的 MyHandler 的内部实例变量,但没有记录,所以我不知道它在哪里。

也许我错过了一些基本的东西,但我只是想不出实现这一目标的“正确”方法

python socketserver
2个回答
1
投票

解决此 API 限制的一种方法是使用类工厂函数:

def get_handler(fname):
    class MyHandler(BaseRequestHandler):
        def handle(self):
            while data := self.request.recv(RECVBUF):
                print(data.decode(), end="")
                # copy it to file, I dunno =)
                with open(fname, 'wb') as f:
                    f.write(data)
    return MyHandler

然后像这样使用它:

    with TCPServer(ADDR, get_handler('/tmp/file.bin')) as tcpserver:

0
投票

另一个可能的选择是查看内部 socket 模块 API 和子类

TCPServer
来覆盖
finish_request()
方法:

class MyTCPServer(TCPServer):
    finish_request(self, request, client_address):
        """ That's the only method where handler is used:
            could be easily overridden
        """
        self.RequestHandlerClass(request, client_address, self)

从这里出发还有很多路要走:

  • 引入类似
    MyTCPServer.filename
    属性的内容并将其作为另一个构造函数参数传递给
    RequestHandlerClass
  • 用函数调用或可调用类实例调用替换处理程序类实例化
  • 也许会覆盖
    __init__()
    ,本质上将
    self.RequestHandlerClass
    重命名为更明确,如果它不再被视为类的话

总体而言,

BaseRequestHandler
API 非常简单且易于扩展。

© www.soinside.com 2019 - 2024. All rights reserved.