【问题标题】:How to get the current port number in Flask?如何在 Flask 中获取当前端口号?
【发布时间】:2011-07-02 10:07:33
【问题描述】:

使用Flask,如何获取flask当前连接的端口号?我想使用端口 0 在随机端口上启动服务器,但我还需要知道我在哪个端口上。

编辑

我想我已经找到了解决我的问题的方法,尽管它不是问题的答案。我可以遍历以 49152 开头的端口,并尝试通过 app.run(port=PORT) 使用该端口。我可以在 try catch 块中执行此操作,这样如果我收到 Address already in use 错误,我可以尝试下一个端口。

【问题讨论】:

  • 在运行示例 Flask 程序时出现此错误:.....文件“/usr/lib/python2.7/socket.py”,第 228 行,方法返回 getattr(self ._sock,name)(*args) socket.error: [Errno 13] Permission denied 请帮助我如何更改端口

标签: python networking web-frameworks flask


【解决方案1】:

你不能轻易获得 Flask 使用的服务器套接字,因为它隐藏在标准库的内部(Flask 使用 Werkzeug,其开发服务器基于 stdlib 的BaseHTTPServer)。

但是,您可以自己创建一个临时端口,然后关闭创建它的套接字,然后自己使用该端口。例如:

# hello.py
from flask import Flask, request
import socket

app = Flask(__name__)

@app.route('/')
def hello():
    return 'Hello, world! running on %s' % request.host

if __name__ == '__main__':
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.bind(('localhost', 0))
    port = sock.getsockname()[1]
    sock.close()
    app.run(port=port)

会给你要使用的端口号。运行示例:

$ python hello.py 
* Running on http://127.0.0.1:34447/

并且,在浏览到 http://localhost:34447/ 时,我看到了

你好,世界!在 localhost:34447 上运行

在我的浏览器中。

当然,如果在您关闭套接字和 Flask 使用该端口打开套接字之间有其他东西使用该端口,您会收到“正在使用的地址”错误,但您可以在您的环境。

【讨论】:

    【解决方案2】:

    正如@VinaySajip Flask 所指出的,它使用标准服务器套接字,但它从不将实例分配给任何变量,只需构造它并在行中调用serve_forever()

    无论如何,如@ThiefMaster 所说,尝试从flask 的应用程序中提取套接字,我们可以拦截bind_socket() 调用并读取地址,而无需处理并发套接字创建。这是我的解决方案:

    from flask import Flask, request
    import socketserver
    
    app = Flask("")
    
    original_socket_bind = SocketServer.TCPServer.server_bind
    def socket_bind_wrapper(self):
        ret = original_socket_bind(self)
        print("Socket running at {}:{}".format(*self.socket.getsockname()))
        # Recover original implementation
        socketserver.TCPServer.server_bind = original_socket_bind
        return ret
    
    @app.route("/")
    def hello():
        return 'Hello, world! running on {}'.format(request.host)
    
    socketserver.TCPServer.server_bind = socket_bind_wrapper   #Hook the wrapper
    app.run(port=0, debug=True)
    

    【讨论】:

    • 这是一个很好的方法,但它有一个缺点。当使用更多使用 TCPServer 的代码时,您的调用也将被调用,并且您将多次调用您的函数
    • @Fruch 你是对的,这只是一个概念证明。也许在with 上下文管理器中使用mock.patch 以安全和干净的方式进行操作会更好。
    【解决方案3】:

    我同意接受的答案

    你不能轻易获得 Flask 使用的服务器套接字,因为它隐藏在标准库的内部(Flask 使用 Werkzeug,其开发服务器基于 stdlib 的 BaseHTTPServer)。

    但是,我刚刚发现 Werkzeug 在运行werkzeug.serving.BaseWSGIServer 时公开了一个选项来传递文件描述符以用作它的套接字(这就是flask 的run() 函数最终所做的事情)。使用此功能,可以选择一个随机可用端口,然后告诉 Werkzeug 使用它。

    这是我的应用程序的启动:

    import logging
    import pprint
    import sys
    from flask import Flask
    
    applog = logging.getLogger(__name__)
    applog.setLevel(logging.INFO)
    app = Flask(__name__)
    
    if __name__ == '__main__':
    
        app.config.from_object('myapp.default_settings')
        try:
            app.config.from_envvar('MYAPP_SETTINGS')
        except RuntimeError:
            applog.warning("MYAPP_SETTINGS environment variable not set or invalid. Using default settings.")
    
        # SERVER_NAME should be in the form of localhost:5000, 127.0.0.1:0, etc...
        # So, split on : and take the second element as the port
        # If the port is 0, we need to pick a random port and then tell the server to use that socket
        if app.config['SERVER_NAME'] and int(app.config['SERVER_NAME'].split(':')[1]) == 0:
            import socket, os
    
            # Chose a random available port by binding to port 0
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.bind((app.config['SERVER_NAME'].split(':')[0], 0))
            sock.listen()
    
            # Tells the underlying WERKZEUG server to use the socket we just created
            os.environ['WERKZEUG_SERVER_FD'] = str(sock.fileno())
    
            # Update the configuration so it matches with the port we just chose
            # (sock.getsockname will return the actual port being used, not 0)
            app.config['SERVER_NAME'] = '%s:%d' % (sock.getsockname())
    
        # Optionally write the current port to a file on the filesystem
        if app.config['CREATE_PORT_FILE']:
            with open(app.config['PORT_FILE'], 'w') as port_file:
                port_file.write(app.config['SERVER_NAME'].split(':')[1])
    
        applog.info("Running app on %s" % (app.config['SERVER_NAME']))
        applog.info("Active Settings:%s" % pprint.pformat(app.config, compact=True))
        app.run()
    

    myapp.default_settings的内容:

    SERVER_NAME = '127.0.0.1:0'
    PORT_FILE = 'current_port'
    CREATE_PORT_FILE = True
    

    这里重要的一点是设置os.environ['WERKZEUG_SERVER_FD']。 Werkzeug 在run_simple 函数期间查看此环境变量,如果已定义,则将其作为fd 参数传递给make_server 函数。这最终被用作通信的套接字。

    虽然我不能保证这种方法的稳定性(我不知道WERKZEUG_SERVER_FD 环境变量的支持程度),但到目前为止,我更喜欢它而不是建议的解决方案,因为:

    1. 我不必遍历一系列捕获异常的端口,我只需直接从操作系统获取第一个可用端口。
    2. 在我绑定随机端口和运行应用程序之间,我选择的随机端口不可能被占用,因为我绑定的端口是我的应用程序最终使用的端口。

    上面的代码还记录正在使用的端口,并可选择将当前正在使用的端口写入配置选项指定的文件中。

    【讨论】:

      【解决方案4】:

      如今,大多数操作系统都有一个名为 netstat 的命令,它会为您提供当前正在使用的所有端口的列表,如果您问得足够好,还会在这些端口上运行哪些应用程序。 :)

      使用os.popensubprocess 模块应该很容易解析。

      除此之外,您可以在启动每个服务器时跟踪您正在使用的端口...

      另外,如果您碰巧在 http 请求中执行此操作,您可以查看 wsgi 环境中的 SERVER_PORT cgi 变量。

      【讨论】:

      • 我觉得一定有比这更好的方法。
      【解决方案5】:

      绑定到端口 0 是正确的。这将使操作系统为您选择一个介于 1024 和 65535 之间的可用端口。

      要在绑定后检索所选端口,请使用your_socket.getsockname()[1]

      所以你只需要了解如何访问 Flask 使用的侦听套接字。

      socket.getsockname() 文档:http://docs.python.org/library/socket.html#socket.socket.getsockname

      【讨论】:

      • 不是那么简单.... Flask 不存储HTTPServer 实例,而只是创建它然后调用serve_forever()
      猜你喜欢
      • 2013-01-22
      • 1970-01-01
      • 2022-01-22
      • 1970-01-01
      • 1970-01-01
      • 2019-11-22
      • 2016-04-19
      • 1970-01-01
      • 2012-09-15
      相关资源
      最近更新 更多