Как узнать текущий номер порта во Flask? - PullRequest
18 голосов
/ 23 февраля 2011

Используя Flask , как я могу получить текущий номер порта, к которому подключена колба?Я хочу запустить сервер на случайном порту, используя порт 0, но мне также нужно знать, на каком я порту.

Редактировать

Я думаю, что нашелобойти мою проблему, хотя это не ответ на вопрос.Я могу перебирать порты, начиная с 49152, и пытаться использовать этот порт через app.run(port=PORT).Я могу сделать это в блоке try catch, чтобы в случае ошибки Address already in use я мог попробовать следующий порт.

Ответы [ 5 ]

38 голосов
/ 23 февраля 2011

Вы не можете легко добраться до сокета сервера, используемого Flask, так как он скрыт во внутренностях стандартной библиотеки (Flask использует Werkzeug, сервер разработки которого основан на BaseHTTPServer) stdlib.

Тем не менее, вы можете создать временный порт самостоятельно и затем закрыть сокет, который его создает, а затем использовать этот порт самостоятельно.Например:

# 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, открывая сокет с этим портом, вы 'я получил ошибку «Адрес используется», но вы можете использовать эту технику в своей среде.

3 голосов
/ 22 декабря 2014

Как указывает @VinaySajip, Flask использует стандартный серверный сокет, но никогда не назначает экземпляр какой-либо переменной, просто сконструируйте его и вызовите serve_forever() в строке.

В любом случае, вместо того, чтобы попытаться извлечь сокет из приложения фляги, как сказано @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)
1 голос
/ 25 августа 2018

Я согласен с принятым ответом, что

Вы не можете легко добраться до сокета сервера, используемого Flask, так как он скрыт во внутренностях стандартной библиотеки (Flask использует Werkzeug, чья разработкасервер основан на BaseHTTPServer stdlib).

Однако я только что обнаружил, что Werkzeug предоставляет возможность передавать дескриптор файла для использования в качестве сокета при запуске werkzeug.serving.BaseWSGIServer (что и есть в колбе * 1008).* функция в конечном итоге делает).Используя эту функциональность, можно выбрать произвольно доступный порт, а затем указать 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. Нетвероятность того, что выбранный мной случайный порт будет использован между моментом, когда я связываю случайный порт, и когда я запускаю приложение, потому что порт, который я связываю, является портом, который заканчивается моим приложением.

Приведенный выше код также регистрирует журналиспользуемый порт и дополнительно записывает текущий используемый порт в файл, указанный параметром конфигурации.

1 голос
/ 23 февраля 2011

Привязка к порту 0 правильная. Это заставит операционную систему выбрать для вас доступный порт между 1024 и 65535.

Чтобы получить выбранный порт после привязки, используйте your_socket.getsockname()[1].

Итак, все, что вам нужно выяснить, - это получить доступ к гнезду прослушивания, которое использует Flask.

socket.getsockname() документы: http://docs.python.org/library/socket.html#socket.socket.getsockname

0 голосов
/ 23 февраля 2011

В большинстве операционных систем в наши дни есть команда под названием netstat, которая выдаст вам список всех портов, которые используются в данный момент, а также какие приложения работают на них, если вы спросите достаточно хорошо. :)

Это должно быть довольно легко разобрать с os.popen или subprocess модулем.

Кроме того, вы можете просто отслеживать, какие порты вы используете при запуске каждого сервера ...

Кроме того, если вы делаете это из http-запроса, вы можете посмотреть переменную SERVER_PORT cgi из среды wsgi.

...