OS レベルで TCP 設定を微調整することでハング状態を取り除くことができると思いますが、アプリケーションが専用の (そして保守可能な) マシンで動作しないと仮定すると、より一般的な解決策を探す必要があります。
あなたは尋ねました:
おそらく一定時間ソケットを介してデータが送受信されていない場合にのみタイムアウトすることは可能ですか?
そして、これはまさにsocket.settimeout
(または に渡されたものurllib2
) が与える振る舞いです。SIGALRM に基づくタイムアウト (遅いデータ転送中でも終了する) とは対照的に、ソケットに渡されるタイムアウトは、定義された期間中にデータが送信されなかった場合にのみ発生します。socket.send
orの呼び出しはsocket.recv
、期間中にすべてのデータではなく一部のデータが送信された場合、部分的なカウントを返す必要がありurllib2
、残りのデータを送信するために後続の呼び出しを使用します。
これを言うと、POST 呼び出しが複数のsend
呼び出しで実行され、データを送信せずにブロックされてタイムアウトになる場合、アップロードの途中で POST 呼び出しが終了する可能性があります。アプリケーションによって適切に処理されないという印象を与えましたが、プロセスの強制終了または単に接続の切断に似ているため、適切に処理されるべきだと思います。
socket.settimeout
問題が解決しないことをテストして確認しましたか? それとも、動作がどのように実装されているかわかりませんでしたか? 前者が正しい場合、もう少し詳細を教えてください。タイムアウトを設定するだけで安全だと確信しています。Pythonは、動作が上記のような低レベルのBSDソケット実装を使用しているだけだからです。さらに参考資料を提供するには、setsockopt
man ページSO_RCVTIMEO
やSO_SNDTIMEO
オプションを参照してください。socket.settimeout
これらの機能とオプションを正確に使用することを期待しています。
--- 編集 --- (いくつかのテスト コードを提供するため)
そのため、モジュールを入手しRequests
て動作をテストすることができましたurllib2
。recv
すべての呼び出しの間隔を広げてデータのブロックを受信していたサーバーを実行しました。予想どおり、間隔が指定されたタイムアウトに達すると、クライアントはタイムアウトしました。例:
サーバ
import socket
import time
listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
listener.bind(("localhost", 12346))
listener.listen(1)
sock,_ = listener.accept()
interval = 0.5
while 1:
interval += 1 # increase interval by 1 second
time.sleep(interval)
# Get 1MB but will be really limited by the buffer
data = sock.recv(1000000)
print interval, len(data)
if not data:
break
クライアント (リクエストモジュール)
import requests
data = "x"*100000000 # 100MB beefy chunk
requests.post("http://localhost:12346", data=data, timeout=4)
クライアント (urllib2 モジュール)
import urllib2
data = "x"*100000000 # 100MB beefy chunk
urllib2.urlopen("http://localhost:12346", data=data, timeout=4)
出力 (サーバー)
> 1.5 522832
> 2.5 645816
> 3.5 646180
> 4.5 637832 <--- Here the client dies (4.5 seconds without data transfer)
> 5.5 294444
> 6.5 0
両方のクライアントで例外が発生しました:
# urllib2
URLError: timeout('timed out',)
# Requests
Timeout: TimeoutError("HTTPConnectionPool(host='localhost', port=12346): Request timed out. (timeout=4)",)
すべてが期待どおりに機能します!引数としてタイムアウトを渡さない場合urllib2
も、 でうまく反応しましたがsocket.setdefaulttimeout
、反応Requests
しませんでした。内部実装はデフォルト値をまったく使用する必要がなく、渡された引数に応じて単純に上書きしたり、非ブロッキング ソケットを使用したりできるため、驚くことではありません。
私は以下を使用してこれを実行しています:
OSX 10.8.3
Python 2.7.2
Requests 1.1.0