SSブログ

TCP通信の監視にパケット・モニタを使ってみよう [ColdFire V2]このエントリーを含むはてなブックマーク#

2047506

付録基板との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で受信確認します。

このように、サーバが再送信を行えば通信が成り立ち、クライアントはすべての受信することができます。

本日の考察

以上の結果から、以下の事柄が判明、推測されました。

  1. SilentCから送信されるパケットのウィンドウサイズは、いつも1454バイトである。
  2. 受信確認(ACK)が届いていないにもかかわらず、PCからセグメントが再送されない場合がある。
  3. SilentCが送信するべき受信確認パケットが「しばしば」届かない。
  4. 再送信セグメントに対するSilentCの応答が不適切な場合がある。
  5. パケットひとつひとつに対してハンド・シェイクを行えば、欠損無く通信ができるらしい。

参考文献

Interface (インターフェース) 2008年 09月号 [雑誌]

Interface (インターフェース) 2008年 09月号 [雑誌]

  • 作者:
  • 出版社/メーカー: CQ出版
  • 発売日: 2008/07/25
  • メディア: 雑誌

nice!(0)  コメント(4)  トラックバック(0)  このエントリーを含むはてなブックマーク#

nice! 0

コメント 4

hamayan

Wiresharkは元々Ethrealと呼ばれていたパケットモニタで、定評のある物です。
某大手(重)電気メーカーでは、みんなでこれを入れていました(笑)。
別の某大手(重)電気メーカーではsniferでしたが。

#9から#14までの流れは正常です。これはスライディングWindowと呼ばれるもので、受けた側は、チェックした時点での確認応答をまとめてすれば、過去の受信データについても確認した事になるからで、ネットワーク上のトラフィックの削減と、高い転送効率を維持できます。

スライディングWindowを使わないと、TCPの転送なんて、遅くてダメダメです。

しかしそれ以降のSilentC側のやり取りは、完全に破綻していますね。

by hamayan (2008-09-25 06:26) 

hamayan

追伸
パケットモニタはPC自身に出入りするパケットだけでなく、LAN上のパケット全てをモニタできますよ。今時のPCやNICの性能は非常に高いので、ハードウエアの高価なモニタを使う事ってあまり無いのではないかな。

ただLAN上の全てのパケットのモニタを行う場合、スイッチングハブではなくダムハブ(リピーター)を使う必要が有りますけれど。

by hamayan (2008-09-25 07:01) 

noritan

たとえ#11に対する応答が無くても、続く#13に対する応答があれば、#11は受信したとみなすということですね。ネットワークマガジンの過去記事PDFで調べました。

Read関数の返り値を見ると、#11が本当は受信できていないらしいことがわかります。つまり、問題は、SilentC側が#11を正常に受信していないにも関わらず、#13に対する応答を発行してしまっているという事でしょうか。

私が推定する原因は、
* 受信側でシーケンス番号が正しく管理されていない。
* Read関数のFIFO処理に異常がある。
いずれも、ソフトウェアによる処理ですよね。

by noritan (2008-09-25 10:00) 

hamayan

> たとえ#11に対する応答が無くても、・・・

そうです、その通りです。
ただ、あまり長いこと確認応答をしないと送信側はタイムアウトで再送してきますので、その辺の兼ね合いとか、結構面倒な統計処理になります。まあそこまでは私もやっていませんが。

原因は中(ソフトウエア)の話なので判りませんが、noritanさんの推測辺りではないかと。

by hamayan (2008-09-25 10:14) 

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

トラックバック 0

トラックバックの受付は締め切りました

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。