如何获取Flask中的当前端口号?

时间:2011-02-23 00:14:43

标签: python networking web-frameworks flask

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

修改

我认为我找到了解决问题的方法,尽管这不是问题的答案。我可以遍历从49152开始的端口,并尝试通过app.run(port=PORT)使用该端口。我可以在try catch块中执行此操作,以便在出现Address already in use错误时,我可以尝试下一个端口。

5 个答案:

答案 0 :(得分:38)

你无法轻易获得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用该端口打开套接字,则会出现“正在使用的地址”错误,但您可能可以在环境。

答案 1 :(得分:3)

正如@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)

答案 2 :(得分:1)

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

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

所以你需要知道的是如何访问Flask使用的侦听套接字。

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

答案 3 :(得分:0)

现在大多数操作系统都有一个名为netstat的命令,它会为您提供当前正在使用的所有端口的列表,以及如果您提出足够好的请求,它们正在运行哪些应用程序。 :)

使用os.popensubprocess模块进行解析应该非常简单。

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

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

答案 4 :(得分:0)

我同意接受的答案

  

您无法轻易获得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. 在我绑定一个随机端口到运行该应用程序之间,没有机会获得我选择的随机端口,因为我绑定的端口是我的应用程序最终使用的端口。

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

相关问题