Pythonのローカルホストでランダムに開いたTCPポートを取得したい。最も簡単な方法は何ですか?
私の現在の解決策:
def get_open_port():
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(("",0))
s.listen(1)
port = s.getsockname()[1]
s.close()
return port
あまり良くなく、100%正確でもありませんが、今のところは機能します。
空きポートは、オペレーティングシステムによって選択されたポートにソケットをバインドすることで見つけることができます。オペレーティングシステムがポートを選択した後、ソケットを破棄できます。ただし、このソリューションは競合状態に対して耐性がありません。空きポート番号を取得してからこのポートを使用するまでの短い時間に、他のプロセスがこのポートを使用する可能性があります。
def find_free_port():
s = socket.socket()
s.bind(('', 0)) # Bind to a free port provided by the Host.
return s.getsockname()[1] # Return the port number assigned.
私は実際に自分のプログラムの1つで以下を使用しています。
port = random.randint(10000,60000)
もちろん、これはあなたが持っているコードよりも衝突が起こりやすい傾向があります。しかし、私はそれで問題を抱えたことはありません。重要なのは、いつでも、これらの番号の大きいポートのほとんどが使用されておらず、ランダムに1つだけ選択した場合、別のプロセスと競合する可能性はほとんどないということです。回答に投稿した解決策のようなこと(ソケットを開いてそのポート番号を取得する)を行う場合、ポートが競合しないことはほぼ確実です。したがって、これが(一般に公開するものではなく)自分だけで使用するものである場合は、真に防弾のソリューションを考え出す価値があるかどうかを検討してください。オッズはそれが違いを生むことは決してないでしょう。
あなたの質問に対するMarceloCantosのコメントに動機付けられて、このような場合の標準的な解決策は、ポートを使用するプロセスをそれにバインドし、その情報をそれを必要とする他のプログラムと共有することです。通常、ポート番号を含む一時ファイルをファイルシステムの標準的な場所に書き込むようなことを行います。あなたが取り組んでいるプロセスはそれを行わないので、ある意味であなたが思いつくどんな解決策も少しハックになるでしょう。しかし、繰り返しになりますが、それがあなた自身の使用のためだけであれば、それはおそらく問題ありません。
エフェメラルポートは基本的に49152〜65535の範囲にあります。より広い範囲のポートをチェックする場合は、randintの値を変更するだけです。
import pustil
from random import randint
def getfreeport():
port = randint(49152,65535)
portsinuse=[]
while True:
conns = pstuil.net_connections()
for conn in conns:
portsinuse.append(con.laddr[1])
if port in portsinuse:
port = randint(49152,65535)
else:
break
return port
これは私のバージョンですが、ポート範囲を指定しても実際にはランダムではありません。これも競合状態に悩まされますが、事前に港を知る必要がある場合、これが私が知る最良の方法です。
import socket
import errno
import contextlib
reserved_ports = set()
def get_open_port(lowest_port = 0, highest_port = None, bind_address = '', *socket_args, **socket_kwargs):
if highest_port is None:
highest_port = lowest_port + 100
while lowest_port < highest_port:
if lowest_port not in reserved_ports:
try:
with contextlib.closing(socket.socket(*socket_args, **socket_kwargs)) as my_socket:
my_socket.bind((bind_address, lowest_port))
this_port = my_socket.getsockname()[1]
reserved_ports.add(this_port)
return this_port
except socket.error as error:
if not error.errno == errno.EADDRINUSE:
raise
assert not lowest_port == 0
reserved_ports.add(lowest_port)
lowest_port += 1
raise Exception('Could not find open port')