ノンブロッキングのソケット connect() を取得するにはどうすればよいですか?
-
05-07-2019 - |
質問
ここで非常に単純な問題があります。多くのホストと同時に通信する必要がありますが、各リクエストは十分に独立しているため、実際には同期する必要はありません。
そのため、私はスレッドをスパム送信するのではなく、非同期ソケットを使用することを選択しました。さて、ちょっとした問題があります:
非同期機能は魅力的に機能しますが、100 台のホストに接続し、100 回のタイムアウト (タイムアウト = 10 秒) が発生した場合、すべての接続が失敗したことを確認するために 1000 秒待機します。
非ブロッキングソケット接続も取得する方法はありますか?私のソケットはすでに nonBlocking に設定されていますが、connect() の呼び出しは依然としてブロックされています。
タイムアウトを減らすことは受け入れられる解決策ではありません。
私はPythonでこれを行っていますが、この場合プログラミング言語はあまり重要ではないと思います。
本当にスレッドを使用する必要がありますか?
解決
タイムアウトを設定するとソケットがブロックするため、接続も並列化する必要があります。または、タイムアウトを設定できず、選択モジュールを使用できませんでした。
asyncore モジュールのディスパッチャクラスを使用してこれを行うことができます。 。基本的な httpクライアントの例をご覧ください。 。そのクラスの複数のインスタンスは、接続時に互いにブロックしません。スレッドを使用してこれを簡単に行うことができ、ソケットタイムアウトの追跡が容易になると思いますが、既に非同期メソッドを使用しているため、同じトラックに留まることもできます。
例として、以下はすべてのLinuxシステムで動作します
import asyncore, socket
class client(asyncore.dispatcher):
def __init__(self, host):
self.host = host
asyncore.dispatcher.__init__(self)
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.connect((host, 22))
def handle_connect(self):
print 'Connected to', self.host
def handle_close(self):
self.close()
def handle_write(self):
self.send('')
def handle_read(self):
print ' ', self.recv(1024)
clients = []
for i in range(50, 100):
clients.append(client('cluster%d' % i))
asyncore.loop()
cluster50-cluster100には、応答しない、または存在しないマシンが多数あります。これにより、すぐに印刷が開始されます。
Connected to cluster50
SSH-2.0-OpenSSH_4.3
Connected to cluster51
SSH-2.0-OpenSSH_4.3
Connected to cluster52
SSH-2.0-OpenSSH_4.3
Connected to cluster60
SSH-2.0-OpenSSH_4.3
Connected to cluster61
SSH-2.0-OpenSSH_4.3
...
ただし、これはブロックする必要があるgetaddrinfoを考慮しません。 DNSクエリの解決に問題がある場合は、すべてを待つ必要があります。おそらく、DNSクエリを独自に個別に収集し、非同期ループでIPアドレスを使用する必要があります
asyncoreよりも大きなツールキットが必要な場合は、 Twisted Matrix をご覧ください。入るのは少し重いですが、pythonで入手できる最高のネットワークプログラミングツールキットです。
他のヒント
使用 select
モジュール。これにより、複数の非ブロッキングソケットで I/O の完了を待つことができます。こちらです さらに詳しい情報 選択時。リンク先のページから:
Cでコーディングすると、
select
かなり複雑です。Pythonでは、それはケーキですが、Cバージョンに十分に近いので、PythonでSelectを理解している場合、Cではほとんど問題がありません。
ready_to_read, ready_to_write, in_error = select.select(
potential_readers,
potential_writers,
potential_errs,
timeout)
あなたは合格します
select
3 つのリスト:最初には、読んでみたいと思うかもしれないすべてのソケットが含まれています。2番目のソケットは、書きたいと思うかもしれませんが、エラーをチェックしたい最後の(通常は空のままにしてください)。ソケットは複数のリストに入ることができることに注意してください。のselect
通話はブロックされていますが、タイムアウトを与えることができます。これは一般に賢明なことです - あなたが別のことをする正当な理由がない限り、それを素敵な長いタイムアウト(たとえば1分)を与えてください。代わりに、3 つのリストを取得します。彼らは、実際に読みやすく、書き込み可能で、誤っているソケットを持っています。これらの各リストは、渡された対応するリストのサブセット(おそらく空)です。また、ソケットを複数の入力リストに配置すると、1つの出力リストに(せいぜい)のみ(せいぜい)になります。
ソケットが出力の読み取り可能なリストにある場合、あなたはこれまでにないように、かつてないほどのビジネスであることができます。
recv
そのソケットに何かが返されます。書き込み可能なリストについても同じアイデア。できるようになりますsend
何か。たぶん、あなたが望んでいるすべてではありませんが、何かが何もないよりはましです。(実際、合理的に健康なソケットは書き込み可能に戻ります - それは、アウトバウンドネットワークバッファースペースが利用可能になることを意味します。)「サーバー」ソケットがある場合は、Potential_readersリストに載せます。それが読み取り可能なリストに登場する場合、あなたの受け入れは(ほぼ確実に)機能します。他の誰かに接続するための新しいソケットを作成した場合は、潜在的な_writersリストに入れてください。書き込み可能なリストに表示されている場合、接続されている可能性があります。
残念ながら、バグを示すコード例がないため、このブロックがどこから来たのかを見るのは少し難しいです。
彼は次のようなことをします:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setblocking(0)
s.connect(("www.nonexistingname.org", 80))
ソケットモジュールは内部でgetaddrinfoを使用します。これは、特にホスト名が存在しない場合のブロッキング操作です。標準に準拠したDNSクライアントは、名前が実際に存在しないか、低速のDNSサーバーのみが関係しているかどうかを確認するために、しばらく待機します。
解決策は、IPアドレスのみに接続するか、 pydns 。
ツイストを使用します。
これは、Pythonで記述された非同期ネットワークエンジンであり、多数のプロトコルをサポートしており、独自のプロトコルを追加できます。クライアントとサーバーの開発に使用できます。接続時にブロックしません。
asyncore モジュールを見ましたか?必要なものだけかもしれません。