TCP通信の監視にパケット・モニタを使ってみよう [ColdFire V2]
付録基板とのTCP通信でデータの欠損が出ることがわかりました。 なぜ、欠損が発生するかを調べるために、パケット・モニタを入れてみました。
あ~あ、またインストールしちゃったよ。
パケット・モニタって、こんなもの
パケット・モニタとは、ネットワークに流れるパケットを捕まえてログを作成するものと私は理解しています。 本格的なハードウェアなら、本当にネットワークの配線に仕掛けるべきなのですが、さすがにそこまでの出費は出来ません。 しかし、PCのetherポートを監視するソフトウェアであれば、拾ってくることも出来ます。 この場合、PCに出入りするパケットしか監視できませんが、まあ、十分でしょう。
今回インストールしたのは、Wiresharkという名前の洋物ソフトウェアです。
接続、点火(?)
サーバ側プログラムをPCで立ち上げた所から監視を開始しました。 SilentCのプログラムを走らせると、TCPによるコネクションが確立されます。
#3 time=15.463291 from=192.168.1.10 to=192.168.1.2 TCP 1024 > 30049 [SYN] Seq=0 Win=1454 Len=0 #4 time=15.463458 from=192.168.1.2 to=192.168.1.10 TCP 30049 > 1024 [SYN, ACK] Seq=0 Ack=1 Win=16616 Len=0 MSS=1460 #5 time=15.463789 from=192.168.1.10 to=192.168.1.2 TCP 1024 > 30049 [ACK] Seq=1 Ack=1 Win=1454 Len=0
まあ、軽いご挨拶ですね。
100バイトのデータを要求した
最初は、100バイトの小さいデータを要求してみました。
> 100 0064 fffe 100
#6 21.353930 192.168.1.10 192.168.1.2 TCP 1024 > 30049 [PSH, ACK] Seq=1 Ack=1 Win=1454 Len=4 #7 21.354438 192.168.1.2 192.168.1.10 TCP 30049 > 1024 [PSH, ACK] Seq=1 Ack=5 Win=16612 Len=100 #8 21.354945 192.168.1.10 192.168.1.2 TCP 1024 > 30049 [ACK] Seq=5 Ack=101 Win=1454 Len=0
#6で、"100\r"という4バイトのデータをクライアントからサーバに送ります。 すると、サーバからは、#7として、受信確認(ACK)兼データがクライアントに送られてきます。 クライアントは、#8で受信確認(ACK)をサーバに送ります。
このとき、#7のSeq=1とLen=100を足した値がAck=101に入ってくるので、サーバは、100バイトのデータが受信されたことを確認することが出来るという仕組みです。
1600バイトのデータを要求したら
次に、1600バイトの大きなデータを要求します。
> 1600 0218 0210 fffe 1064
#9 33.774452 192.168.1.10 192.168.1.2 TCP 1024 > 30049 [PSH, ACK] Seq=5 Ack=101 Win=1454 Len=5 #10 33.775003 192.168.1.2 192.168.1.10 TCP 30049 > 1024 [ACK] Seq=101 Ack=10 Win=16607 Len=536 #11 33.775031 192.168.1.2 192.168.1.10 TCP 30049 > 1024 [ACK] Seq=637 Ack=10 Win=16607 Len=536 #12 33.778026 192.168.1.10 192.168.1.2 TCP 1024 > 30049 [ACK] Seq=10 Ack=637 Win=1454 Len=0
#9で、クライアントからサーバに"1600\r"という5バイトのデータが送信されます。 これに対して、サーバは、1600バイトのデータを三つに分割し、#10と#11で536バイトずつのデータを連続して送ります。 その間隔は、わずかに30マイクロ秒です。 クライアントは、#10に対する応答を#12で返します。 #12のAck=637が、#10のSeq=101とLen=536の和になっていることから#10に対する応答であることがわかります。 この時点では、#11に対する応答は、まだPCに届いていません。
#13 33.778113 192.168.1.2 192.168.1.10 TCP 30049 > 1024 [PSH, ACK] Seq=1173 Ack=10 Win=16607 Len=528 #14 33.783299 192.168.1.10 192.168.1.2 TCP 1024 > 30049 [ACK] Seq=10 Ack=1701 Win=1454 Len=0
続いて、サーバからは#13で最後の528バイトが送信されてきます。 その後、#14でAck=1701の応答をクライアントが返しています。 Ack=1701は、#13のSeq=1173とLen=528の和なので、#13に対する応答です。
これで、一連の通信は終わってしまいました。 あれ? #11に対する応答がありません。 SilentCの出力したログを見ても、二つ目のパケットを受け取ったとは思っていないことがわかります。 この場合、サーバは、#11のデータを再送するんじゃないのかな?
受信確認がスキップされたとしても、TCPのプロトコルとしては正しいのだそうです。 問題は、SilentCが受信したことを認識していないことです。
また、クライアントが送っているパケット(セグメント?)には、いつもWin=1454が付いています。 536バイトのデータを受信していても、ウィンドウの値は1454のままです。 これを信じたサーバが山のようにパケットを送ってくるようにならないのでしょうか。
1600バイトのデータを要求して、再送が発生した
続いて、1600バイトのデータを再度要求してみました。
> 1600 0218 0218 fffe 1072
#15 46.617221 192.168.1.10 192.168.1.2 TCP 1024 > 30049 [PSH, ACK] Seq=10 Ack=1701 Win=1454 Len=5 #16 46.617718 192.168.1.2 192.168.1.10 TCP 30049 > 1024 [ACK] Seq=1701 Ack=15 Win=16602 Len=536 #17 46.617753 192.168.1.2 192.168.1.10 TCP 30049 > 1024 [ACK] Seq=2237 Ack=15 Win=16602 Len=536 #18 46.618920 192.168.1.10 192.168.1.2 TCP 1024 > 30049 [ACK] Seq=15 Ack=2773 Win=1454 Len=0 #19 46.619007 192.168.1.2 192.168.1.10 TCP 30049 > 1024 [PSH, ACK] Seq=2773 Ack=15 Win=16602 Len=528
クライアントからの#15の要求に対して、サーバから#16、#17、#19と三つのセグメントが送られてきます。 クライアントは、#18で受信確認を返します。 この受信確認は、Ack=2773なので、#17のSeq=2237、Len=536に対する応答で、#16に対する応答はPCに届いていません。
#20 46.625991 192.168.1.10 192.168.1.2 TCP [TCP Dup ACK 18#1] 1024 > 30049 [ACK] Seq=15 Ack=2773 Win=1454 Len=0 #21 47.752636 192.168.1.2 192.168.1.10 TCP [TCP Retransmission] 30049 > 1024 [PSH, ACK] Seq=2773 Ack=15 Win=16602 Len=528 #22 47.753711 192.168.1.10 192.168.1.2 TCP [TCP Dup ACK 18#2] 1024 > 30049 [ACK] Seq=15 Ack=2773 Win=1454 Len=0 #23 47.753795 192.168.1.2 192.168.1.10 TCP [TCP Fast Retransmission] 30049 > 1024 [PSH, ACK] Seq=2773 Ack=15 Win=16602 Len=528 #24 47.754853 192.168.1.10 192.168.1.2 TCP [TCP Dup ACK 18#3] 1024 > 30049 [ACK] Seq=15 Ack=2773 Win=1454 Len=0 #25 50.067080 192.168.1.2 192.168.1.10 TCP [TCP Retransmission] 30049 > 1024 [PSH, ACK] Seq=2773 Ack=15 Win=16602 Len=528 #26 50.068148 192.168.1.10 192.168.1.2 TCP [TCP Dup ACK 18#4] 1024 > 30049 [ACK] Seq=15 Ack=2773 Win=1454 Len=0 #27 54.895261 192.168.1.2 192.168.1.10 TCP [TCP Retransmission] 30049 > 1024 [PSH, ACK] Seq=2773 Ack=15 Win=16602 Len=528 #28 54.896348 192.168.1.10 192.168.1.2 TCP [TCP Dup ACK 18#5] 1024 > 30049 [ACK] Seq=15 Ack=2773 Win=1454 Len=0
この後、#20でクライアントがサーバに受信確認を送りますが、この受信確認はAck=2773なので、#19に対する応答ではなく、#17のSeq=2237、Len=536に対する応答です。 そのため、サーバからは何度も#19の再送データが送られてきます。 ところが、それに対してクライアントが送るのは、#17に対する応答です。
クライアントが、受信していないと言い張ることで、サーバは、あきらめてしまったのか、再送も止めてしまいます。 結局、この通信は、不成立になったはずなのですが、サーバもクライアントもプログラムは走り続けます。 変なの。
1600バイトのデータを要求して1600バイトのデータを受け取った
何度か試しているうちに、1600バイトのデータを受信できたケースも出てきました。
> 1600 0218 0218 0210 fffe 1600
#51 83.980854 192.168.1.10 192.168.1.2 TCP 1024 > 30049 [PSH, ACK] Seq=25 Ack=5973 Win=1454 Len=5 #52 83.981367 192.168.1.2 192.168.1.10 TCP 30049 > 1024 [ACK] Seq=6501 Ack=30 Win=16587 Len=536 #53 83.982514 192.168.1.10 192.168.1.2 TCP 1024 > 30049 [ACK] Seq=30 Ack=7037 Win=1454 Len=0 #54 83.982581 192.168.1.2 192.168.1.10 TCP 30049 > 1024 [ACK] Seq=7037 Ack=30 Win=16587 Len=536 #55 83.982606 192.168.1.2 192.168.1.10 TCP 30049 > 1024 [PSH, ACK] Seq=7573 Ack=30 Win=16587 Len=528
クライアントの要求(#51)に対して、サーバは#52のデータを送ります。 サーバが#54で次のセグメントを送る前に、クライアントは#53の受信応答を送ることが出来ました。 サーバは、#54に続いて#55で三つ目のセグメントを送ります。
#56 84.568419 192.168.1.2 192.168.1.10 TCP [TCP Retransmission] 30049 > 1024 [ACK] Seq=7037 Ack=30 Win=16587 Len=536 #57 84.569592 192.168.1.10 192.168.1.2 TCP 1024 > 30049 [ACK] Seq=30 Ack=7573 Win=1454 Len=0 #58 84.569681 192.168.1.2 192.168.1.10 TCP [TCP Retransmission] 30049 > 1024 [PSH, ACK] Seq=7573 Ack=30 Win=16587 Len=528 #59 84.574613 192.168.1.10 192.168.1.2 TCP 1024 > 30049 [ACK] Seq=30 Ack=8101 Win=1454 Len=0
ところが、#54、#55に対する受信応答がクライアントから発信されません。 サーバは、約0.5秒後に#56で#54のセグメントを再送し、クライアントは#57で受信確認します。 さらに、サーバは、#58で#55のセグメントを再送し、クライアントが#59で受信確認します。
このように、サーバが再送信を行えば通信が成り立ち、クライアントはすべての受信することができます。
本日の考察
以上の結果から、以下の事柄が判明、推測されました。
- SilentCから送信されるパケットのウィンドウサイズは、いつも1454バイトである。
- 受信確認(ACK)が届いていないにもかかわらず、PCからセグメントが再送されない場合がある。
- SilentCが送信するべき受信確認パケットが「しばしば」届かない。
- 再送信セグメントに対するSilentCの応答が不適切な場合がある。
- パケットひとつひとつに対してハンド・シェイクを行えば、欠損無く通信ができるらしい。
参考文献
Interface (インターフェース) 2008年 09月号 [雑誌]
- 作者:
- 出版社/メーカー: CQ出版
- 発売日: 2008/07/25
- メディア: 雑誌
Wiresharkは元々Ethrealと呼ばれていたパケットモニタで、定評のある物です。
某大手(重)電気メーカーでは、みんなでこれを入れていました(笑)。
別の某大手(重)電気メーカーではsniferでしたが。
#9から#14までの流れは正常です。これはスライディングWindowと呼ばれるもので、受けた側は、チェックした時点での確認応答をまとめてすれば、過去の受信データについても確認した事になるからで、ネットワーク上のトラフィックの削減と、高い転送効率を維持できます。
スライディングWindowを使わないと、TCPの転送なんて、遅くてダメダメです。
しかしそれ以降のSilentC側のやり取りは、完全に破綻していますね。
by hamayan (2008-09-25 06:26)
追伸
パケットモニタはPC自身に出入りするパケットだけでなく、LAN上のパケット全てをモニタできますよ。今時のPCやNICの性能は非常に高いので、ハードウエアの高価なモニタを使う事ってあまり無いのではないかな。
ただLAN上の全てのパケットのモニタを行う場合、スイッチングハブではなくダムハブ(リピーター)を使う必要が有りますけれど。
by hamayan (2008-09-25 07:01)
たとえ#11に対する応答が無くても、続く#13に対する応答があれば、#11は受信したとみなすということですね。ネットワークマガジンの過去記事PDFで調べました。
Read関数の返り値を見ると、#11が本当は受信できていないらしいことがわかります。つまり、問題は、SilentC側が#11を正常に受信していないにも関わらず、#13に対する応答を発行してしまっているという事でしょうか。
私が推定する原因は、
* 受信側でシーケンス番号が正しく管理されていない。
* Read関数のFIFO処理に異常がある。
いずれも、ソフトウェアによる処理ですよね。
by noritan (2008-09-25 10:00)
> たとえ#11に対する応答が無くても、・・・
そうです、その通りです。
ただ、あまり長いこと確認応答をしないと送信側はタイムアウトで再送してきますので、その辺の兼ね合いとか、結構面倒な統計処理になります。まあそこまでは私もやっていませんが。
原因は中(ソフトウエア)の話なので判りませんが、noritanさんの推測辺りではないかと。
by hamayan (2008-09-25 10:14)