web-dev-qa-db-ja.com

Flaskで現在のポート番号を取得するにはどうすればよいですか?

Flask を使用して、flaskが接続されている現在のポート番号を取得するにはどうすればよいですか?ポート0を使用してランダムなポートでサーバーを起動したいのですが、必要です私がどのポートにいるのかを知るために。

編集

質問に対する答えではありませんが、問題の回避策を見つけたと思います。 49152から始まるポートを反復処理し、app.run(port=PORT)を介してそのポートの使用を試みることができます。これはtrycatchブロックで実行できるので、Address already in useエラー、次のポートを試すことができます。

19
david4dev

Flaskが使用するサーバーソケットは、標準ライブラリの内部に隠されているため、簡単に取得することはできません(FlaskはWerkzeugを使用します。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そのポートでソケットを開く間に他の何かがそのポートを使用する場合、「使用中のアドレス」エラーが発生しますが、可能かもしれませんご使用の環境でこの手法を使用します。

38
Vinay Sajip

@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)
3
Michele d'Amico

私は受け入れられた答えに同意します

Flaskが使用するサーバーソケットは、標準ライブラリの内部に隠されているため、簡単に取得することはできません(Flaskは、開発サーバーがstdlibのBaseHTTPServerに基づいているWerkzeugを使用しています)。

しかし、Werkzeugが_werkzeug.serving.BaseWSGIServer_(フラスコの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. 例外をキャッチするポートの範囲をループする必要はありません。OSから直接最初に使用可能なポートを取得するだけです。
  2. バインドしているポートはアプリケーションが最終的に使用するポートであるため、ランダムポートをバインドしてからアプリケーションを実行するまでの間に、選択したランダムポートが取得される可能性はありません。

上記のコードは、使用されているポートもログに記録し、オプションで、現在使用されているポートを構成オプションで指定されたファイルに書き込みます。

1
FGreg

ポート0へのバインドは正しいです。これにより、オペレーティングシステムは1024〜65535の使用可能なポートを選択するようになります。

バインド後に選択したポートを取得するには、your_socket.getsockname()[1]を使用します。

したがって、知る必要があるのは、リスニングソケットにアクセスする方法Flask usesです。

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

1
ThiefMaster

最近のほとんどのオペレーティングシステムには、netstatというコマンドがあり、現在使用されているすべてのポートのリストと、十分に質問があれば、それらのポートで実行されているアプリケーションが表示されます。 :)

os.popenまたはsubprocessモジュールを使用して解析するのは非常に簡単です。

それとは別に、各サーバーを起動するときに使用しているポートを追跡することもできます...

また、httpリクエスト内からこれを実行している場合は、wsgi環境からSERVER_PORTcgi変数を確認できます。

0
tangentstorm