クライアントから送信している TCP パケットがサーバーに到着していないように見える単純なクライアント サーバーをセットアップしました。
通常はすべて正常に動作しますが、クライアントで 50 のスレッドをスピンアップして同じ小さなデータ パケット (わずか 39 バイト) でサーバーを「同時に」ヒットすると、サーバーがすべてのバイトを受信しないランダムな回数が発生します。さらに奇妙なのは、それらを受信しない方法が非常に一貫していることです... 5バイトしか受信されません。
私はtcpdumpとtcpflowを使用して、両端で何が起こっているかをキャプチャしています ( tcp フローに慣れていない場合は、大量の TCP SYN/ACK/FIN/etc ノイズを TCP ストリームから削除し、送信されたデータを表示するだけです)。いずれかの方向)。
クライアント側では、39 バイトのパケットを起動する 50 のスレッドの場合、完璧に見えます。具体的には、tcpflow (libpcap を使用) は、50 の同一のデータ転送を示しています。
07 B6 00 01 | 00 1E 00 00 | <etc>
私が理解しているように、libpcap/tcpdump はかなり低いレベル (TCP スタックの下) からデータを取得するため、これは、データが正常に送信されたか、少なくともカーネル バッファーにスタックされていなかったことを意味します。
ただし、サーバー側を見ると、すべてが完璧というわけではありません。乱数が失敗しており、高い割合です。たとえば、50 のソケット接続のうち 30 は正常に動作しますが、そのうちの 20 では、サーバーsocket.recv
がバイトの待機中にタイムアウトになるというプロトコル エラーが発生します (プロトコルは正確なパケット長を示します)。
失敗する方法は非常に一貫しています。30/20 の場合、30 個のソケットが送信された 39 バイトを完全に受信します。残りの 20 個の ALL はこの部分的なデータを受け取り、その後socket.recv
タイムアウトします。
07 B6 00 01 | 00
20 接続ごとに 5 バイトしか到着していません。tcpdump も 5 バイトしか到着していないことを示しているため、カーネル レベルにあるようです。
これはどのように起こりますか?
この 5 バイト境界は 100% 一致するわけではありません。これはヘッダーの最初の部分で、次に 34 バイトのペイロードが続きますが、到着していません。クライアント側では、このように分割されます。
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((HOST, PORT))
sock.sendall(HEADER) # 5 bytes
sock.sendall(PAYLOAD) #34 bytes
そして、すべてのスレッドで両方のsock.sendall
呼び出しが正常に完了しました。私の tcp ロギングは、50 回の実行すべてが 39 バイトを完全に「ドアの外」に送信することを示していることが証明されています。
これの根本的な原因に関するアイデアはありますか? 私は何が欠けていますか?