如何在 Flask 中选择随机端口号?

发布于 2024-10-18 21:26:59 字数 333 浏览 3 评论 0原文

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

编辑

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

Using Flask, how can I get the current port number that flask is connected to? I want to start a server on a random port using port 0 but I also need to know which port I am on.

Edit

I think I've found a work around for my issue, although it isn't an answer to the question. I can iterate through ports starting with 49152 and attempt to use that port through app.run(port=PORT). I can do this in a try catch block so that if I get an Address already in use error, I can try the next port.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

活雷疯 2024-10-25 21:26:59

您无法轻松获取 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/ 时,我看到

你好,世界!在本地主机上运行:34447

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

You can't easily get at the server socket used by Flask, as it's hidden in the internals of the standard library (Flask uses Werkzeug, whose development server is based on the stdlib's BaseHTTPServer).

However, you can create an ephemeral port yourself and then close the socket that creates it, then use that port yourself. For example:

# 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)

will give you the port number to use. An example run:

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

and, on browsing to http://localhost:34447/, I see

Hello, world! running on localhost:34447

in my browser.

Of course, if something else uses that port between you closing the socket and then Flask opening the socket with that port, you'd get an "Address in use" error, but you may be able to use this technique in your environment.

萌无敌 2024-10-25 21:26:59

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

无论如何,尝试从 Flask 的应用程序中提取套接字,如 @ThiefMaster 所说,我们可以拦截 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)

As pointed by @VinaySajip Flask use standard server socket but it never assign the instance to any variable, just construct it and call serve_forever() in line.

Anyway instead try to extract socket from flask's app as said @ThiefMaster, we can intercept bind_socket() call and read the address without take care of concurrent socket creation. Here is my solution:

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)
慢慢从新开始 2024-10-25 21:26:59

我同意已接受的答案

您无法轻松获取 Flask 使用的服务器套接字,因为它隐藏在标准库的内部(Flask 使用 Werkzeug,其开发服务器基于 stdlib 的 BaseHTTPServer)。

然而,我刚刚发现 Werkzeug 公开了一个选项来传递文件描述符,以在运行时用作套接字(这就是 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. 在绑定随机端口和运行应用程序之间,我选择的随机端口不可能被占用,因为我绑定的端口是我的应用程序最终使用的端口。

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

I agree with the accepted answer that

You can't easily get at the server socket used by Flask, as it's hidden in the internals of the standard library (Flask uses Werkzeug, whose development server is based on the stdlib's BaseHTTPServer).

However, I just discovered that Werkzeug exposes an option to pass a file descriptor for use as it's socket when running werkzeug.serving.BaseWSGIServer (which is what flask's run() function ends up doing). Using this functionality it is possible to select a random available port and then tell Werkzeug to use it.

Here is the startup of my application:

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()

Contents of myapp.default_settings:

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

The important bit here is setting os.environ['WERKZEUG_SERVER_FD']. Werkzeug looks at this environment variable during the run_simple function and, if it is defined, passes it as the fd parameter to the make_server function. This eventually is used as the socket for communication.

While I can't guarantee the stability of this approach (I don't know how well supported the WERKZEUG_SERVER_FD environment variable is) I prefer it to the suggested solutions so far because:

  1. I don't have to loop through a range of ports catching exceptions, I just get the first available port straight from the OS.
  2. There is no chance my chosen random port gets taken between the time I bind a random port and when I run the application because the port I am binding is the port my application ends up using.

The code above also logs the port being used and optionally writes the current port being used to a file specified by a configuration option.

瞄了个咪的 2024-10-25 21:26:59

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

使用 os.popen 或 subprocess 模块应该很容易解析。

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

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

Most operating systems these days have a command called netstat that will give you a list of all ports that are currently in use, as well as which applications are running on them if you ask nicely enough. :)

It should be pretty easy to parse with os.popen or the subprocess module.

Aside from that, you could just track which ports you're using as you start each server...

Also, if you happen to be doing this from inside an http request, you could look at the SERVER_PORT cgi variable from the wsgi environment.

木落 2024-10-25 21:26:59

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

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

因此,您需要了解的是如何访问 Flask 使用的监听套接字。

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

Binding to port 0 is correct. That will make the operating system choose an available port between 1024 and 65535 for you.

To retrieve the chosen port after binding, use your_socket.getsockname()[1].

So all you need to find out is how to access the listening socket Flask uses.

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文