2017-07-02 20 views
2

私はAppiumサーバーの周りにPythonラッパーを書いています。 Appiumは、ローカルポートをバインドするためのコマンドラインパラメータを受け入れます。残念ながら、Appiumは自身のために空きポートを自動選択できないため、明示的に指定されたポートにバインドするか、EADDRINUSEで失敗します。ポート0にバインドするように指示しても、正常に起動しますが、バインドされたポートは表示されません。サブプロセスの空きポートを選択するにはどうすればよいですか?

私がPythonラッパーで自由なポートを見つけたら、他のプロセスが同じポートにバインドしないという保証はありませんが、私はそれをAppiumに渡しています。私が最初にそれを公開しなければ、Appiumはそれにバインドすることができないので、私はする必要があります。

実際には起こりそうにないのですが、ローカルのポート番号を別のプロセス(Linux、macOS、Windows)に渡す前にローカルポート番号を "予約"する "正しい方法" )?コメント欄で@rodrigo提案へ

+0

ランダムなポートを選択してAppiumに渡し、正しいエラーメッセージがないかどうかを確認することができます。 –

+0

あなたは任意のポートを試すことができませんし、 'EADDRINUSE'を返すとそれを増やし、空きが見つかるまでループしますか? – rodrigo

+0

@AlexHall、これは私が今やっていることです。しかし、問題は「正しい方法」についてです。サブプロセスのポート番号を予約する方法はありますか? – toriningen

答えて

1

おかげで、私はこのコードで終わっている:それは、Linux、MacOSの、およびWindows上で動作します

import platform 
import re 
import subprocess 
from typing import Set 

if platform.system() == 'Windows': 
    def _get_ports(pid): 
     sp = subprocess.run(['netstat', '-anop', 'TCP'], 
          stdout=subprocess.PIPE, 
          stderr=subprocess.DEVNULL, 
          check=True) 

     rx_socket = re.compile(br'''(?x)^
            \s* TCP 
            \s+ 127.0.0.1 : (?P<port>\d{1,5}) 
            \s+ .*? 
            \s+ LISTENING 
            \s+ (?P<pid>\d+) 
            \s* $''') 

     for line in sp.stdout.splitlines(): 
      rxm = rx_socket.match(line) 
      if rxm is None: 
       continue 

      sock_port, sock_pid = map(int, rxm.groups()) 
      if sock_pid == pid: 
       yield sock_port 
else: 
    def _get_ports(pid): 
     sp = subprocess.run(['lsof', '-anlPFn', '+w', 
          f'-p{pid}', '[email protected]', '-sTCP:LISTEN'], 
          stdout=subprocess.PIPE, 
          stderr=subprocess.DEVNULL, 
          check=True) 

     for line in sp.stdout.splitlines(): 
      if line.startswith(b'n'): 
       host, port = line.rsplit(b':', 1) 
       port = int(port) 
       yield port 


def get_ports(pid: int) -> Set[int]: 
    """Get set of local-bound listening TCPv4 ports for given process. 

    :param pid: process ID to inspect 
    :returns: set of ports 
    """ 

    return set(_get_ports(pid)) 

print(get_ports(12345)) 

、及びある特定のプロセスのためのすべてのローカルに結合TCPv4ポートを発見しますLISTEN状態である。また、すべての種類のホスト/ポート/ユーザー名の逆引き検索をスキップして高速化し、昇格された特権を必要としません。

最後に、0.0.0.0:0でAppium(または他のもの)を起動させるという考えは、OSが提供する最初の利用可能なポートにバインドして、現在リッスンしているポートを調べます。競争条件はありません。

1

セレンライブラリは、このトリックを使用しています。

https://github.com/SeleniumHQ/selenium/blob/master/py/selenium/webdriver/common/utils.py#L31

import socket 

def free_port(): 
    """ 
    Determines a free port using sockets. 
    """ 
    free_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
    free_socket.bind(('0.0.0.0', 0)) 
    free_socket.listen(5) 
    port = free_socket.getsockname()[1] 
    free_socket.close() 
    return port 

あなたはポート0にソケットをバインドした場合、カーネルは、空きポートを割り当てます。 WindowsとLinuxで動作します。 TCP/IPのため

https://msdn.microsoft.com/en-us/library/windows/desktop/ms737550.aspx

ポートがゼロとして指定されている場合、サービス・プロバイダ は、動的クライアントポート 範囲からアプリケーションに固有のポートを割り当てます。

http://man7.org/linux/man-pages/man7/ip.7.html

ip_local_port_rangeで、あなたは読むことができます以下:

  • ポート番号で:

    一時ポートは、次の 状況でソケットに割り当てられています 呼び出し時にソケットアドレスは0と指定されます。bind(2);

getsockname()は、どのポートが選択されたかを知るために使用されます。

+0

これは質問に対する回答となるかもしれませんが、いくつかの説明が必要です。このソリューションがどのように、なぜこのソリューションが機能するのか、いくつかの説明で質問を更新してください。 –

+1

あなたのスニペットは最初に利用可能なポートを選択するだけです。これは質問には関係ありません。トリックは、ポートを選択するだけでなく、競合条件を導入することなくサードパーティのサブプロセスに渡すことです。 – toriningen

関連する問題