付録基板同士のクライアント・サーバ・システム [ColdFire V2]
MCF52233付録基板を二枚使って、TCPによるサーバ・クライアント通信をさせてみました。 パケット・モニタでの監視も行います。
サーバは、これだ
サーバは、クライアントから到着したメッセージの末尾に'*'を加えてクライアントに返信します。
main() { char *send_buf,*recv_buf; char soc,newsoc,err_code; send_buf = MemoryAlloc(32); soc = CreateSocket(1); Bind(soc, 30049, 1); for (;;) { newsoc = Accept(soc, -1); if (newsoc < 0) break; do { err_code = Read(newsoc, -1); if (err_code < 0) break; recv_buf = GetReceiveBuffer(newsoc,1); StrCpy(send_buf, recv_buf); StrCat(send_buf, "*"); MemoryFree(recv_buf); Write(newsoc, send_buf, StrLen(send_buf)); err_code = WaitWriteComplete(newsoc); if (err_code < 0) break; } while (0); CloseSocket(newsoc); } CloseSocket(soc); MemoryFree(send_buf); }
クライアントは、これだ
クライアントは、1秒インターバルでサーバに短い電文"I=?"を送り、サーバからの返信を受け取ります。 全部で10回のコネクションを行います。
main() { char *send_buf,*recv_buf; char soc,err_code; long i; send_buf = MemoryAlloc(32); for (i = 0; i < 10; i++) { soc = CreateSocket(1); if (soc < 0) continue; do { err_code = Connect(soc,GetHostByName("192.168.1.103"),30049); if (err_code < 0) break; StrCpy(send_buf, "I="); GetDigit(i, &send_buf[2]); Write(soc, send_buf, StrLen(send_buf)); err_code = WaitWriteComplete(soc); if (err_code < 0) break; err_code = Read(soc, -1); if (err_code < 0) break; recv_buf = GetReceiveBuffer(soc,1); MemoryFree(recv_buf); } while (0); CloseSocket(soc); Sleep(100); } MemoryFree(send_buf); }
リピータ・ハブでパケットのモニタリングもできる
中古のリピータ・ハブ(通称バカ・ハブ)を入手したので、パケット・モニタでクライアントとサーバの通信の様子を見てみます。
#7 2.014995 1024 > 30049 [SYN] Seq=0 Win=1454 Len=0 #8 2.015247 30049 > 1024 [SYN, ACK] Seq=0 Ack=1 Win=1454 Len=0 #9 2.015419 1024 > 30049 [ACK] Seq=1 Ack=1 Win=1454 Len=0
今回は、IPアドレスとプロトコルのカラムを削除しました。 今までどおりの3ウェイ・ハンドシェイクです。 それぞれを Ethernet フレームの単位で詳しく見たところ、60バイトのフレームの最後の6バイトは、 "Trailer" と書いてあり、 00 で埋まっています。 54バイトじゃ、だめなの?
#10 2.019569 1024 > 30049 [PSH, ACK] Seq=1 Ack=1 Win=1454 Len=3 #11 2.019672 30049 > 1024 [ACK] Seq=1 Ack=4 Win=1454 Len=0
#10で"I=0"をサーバに送り、#11でサーバから受信確認が届きます。 いずれのフレームも60バイトで、 Trailer の長さがそれぞれ3バイト、6バイトと異なっています。
#12 2.025681 30049 > 1024 [PSH, ACK] Seq=1 Ack=4 Win=1454 Len=4 #13 2.025938 1024 > 30049 [ACK] Seq=4 Ack=5 Win=1454 Len=0
#12で"I=0*"がサーバから返され、#13でサーバに受信確認が届きます。 いずれのフレームも60バイトで、 Trailer の長さはそれぞれ2バイトと6バイトです。 #12の Trailer には 00 が入っていますが、#13の Trailer には "I=0" が入っています。 何だこりゃ?
#14 2.027416 30049 > 1024 [FIN, ACK] Seq=5 Ack=4 Win=1454 Len=0 #15 2.028981 1024 > 30049 [FIN, ACK] Seq=4 Ack=5 Win=1454 Len=0 #16 2.028990 [TCP Keep-Alive] 1024 > 30049 [ACK] Seq=4 Ack=6 Win=1454 Len=0 #17 2.029080 [TCP Keep-Alive] 30049 > 1024 [ACK] Seq=5 Ack=5 Win=1454 Len=0
コネクションの終了要求は、サーバとクライアントからほぼ同時に発行され、それに対する"ACK"が送信されます。 いずれも60バイトのフレームで、 Trailer に "I=0" と "I=0*" が残ったままです。 なぜ、#16と#17が「TCP Keep-Alive」と判断されたのかは、わかりません。 一言、「This is a TCP keep-alive segment」とだけ表示されています。
#18 2.029693 1024 > 30049 [RST] Seq=5 Win=1454 Len=0
"RST"というのが、初めて出てきました。 コネクションを強制的に終了するのですね。
#19 3.035180 [TCP Port numbers reused] 1024 > 30049 [SYN] Seq=0 Win=1454 Len=0 #20 3.035420 30049 > 1024 [SYN, ACK] Seq=0 Ack=1 Win=1454 Len=0 #21 3.035593 1024 > 30049 [ACK] Seq=1 Ack=1 Win=1454 Len=0 #22 3.039698 1024 > 30049 [PSH, ACK] Seq=1 Ack=1 Win=1454 Len=3 #23 3.039980 30049 > 1024 [ACK] Seq=1 Ack=4 Win=1454 Len=0 #24 3.045840 30049 > 1024 [PSH, ACK] Seq=1 Ack=4 Win=1454 Len=4 #25 3.046068 1024 > 30049 [ACK] Seq=4 Ack=5 Win=1454 Len=0 #26 3.047556 30049 > 1024 [FIN, ACK] Seq=5 Ack=4 Win=1454 Len=0 #27 3.049124 1024 > 30049 [FIN, ACK] Seq=4 Ack=5 Win=1454 Len=0 #28 3.049257 [TCP Keep-Alive] 1024 > 30049 [ACK] Seq=4 Ack=6 Win=1454 Len=0 #29 3.049292 [TCP Keep-Alive] 30049 > 1024 [ACK] Seq=5 Ack=5 Win=1454 Len=0 #30 3.049891 1024 > 30049 [RST] Seq=5 Win=1454 Len=0
二つ目のコネクションは、#22で"I=1"をサーバに送り、#24で"I=1*"をサーバから受け取ります。 #19の「TCP Port numbers reused」でポート番号が再利用されたことがわかります。 いずれのフレームも60バイトでした。
#115 11.196476 1024 > 30049 [SYN] Seq=0 Win=1454 Len=0 #116 11.196695 30049 > 1024 [SYN, ACK] Seq=0 Ack=1 Win=1454 Len=0 #117 11.196900 1024 > 30049 [ACK] Seq=1 Ack=1 Win=1454 Len=0 #118 11.201017 [TCP Retransmission] 1024 > 30049 [PSH, ACK] Seq=1 Ack=1 Win=1454 Len=3 #119 11.201248 30049 > 1024 [ACK] Seq=1 Ack=4 Win=1454 Len=0 #120 11.207180 [TCP Retransmission] 30049 > 1024 [PSH, ACK] Seq=1 Ack=4 Win=1454 Len=4 #121 11.207406 [TCP Keep-Alive] 1024 > 30049 [ACK] Seq=4 Ack=5 Win=1454 Len=0 #122 11.208912 30049 > 1024 [FIN, ACK] Seq=5 Ack=4 Win=1454 Len=0 #123 11.210478 1024 > 30049 [FIN, ACK] Seq=4 Ack=5 Win=1454 Len=0 #124 11.210613 [TCP Keep-Alive] 1024 > 30049 [ACK] Seq=4 Ack=6 Win=1454 Len=0 #125 11.210640 [TCP Keep-Alive] 30049 > 1024 [ACK] Seq=5 Ack=5 Win=1454 Len=0 #126 11.211247 1024 > 30049 [RST] Seq=5 Win=1454 Len=0
順調に通信をこなしましたが、最後の10回目のコネクションで「TCP Retransmission」が発生しています。 Wiresharkの解説によると、#118は#111の、#120は#110の再送と理解されたようです。
#110 10.188839 30049 > 1024 [FIN, ACK] Seq=5 Ack=4 Win=1454 Len=0 #111 10.190404 1024 > 30049 [FIN, ACK] Seq=4 Ack=5 Win=1454 Len=0 #112 10.190462 [TCP Keep-Alive] 1024 > 30049 [ACK] Seq=4 Ack=6 Win=1454 Len=0 #113 10.190534 [TCP Keep-Alive] 30049 > 1024 [ACK] Seq=5 Ack=5 Win=1454 Len=0 #114 10.191173 1024 > 30049 [RST] Seq=5 Win=1454 Len=0
でも、#110と#111は、前回のコネクションの終了要求パケットですよ。 しかも#114で"RST"まで発行されているし。 想像するに、ポート番号の使い回しが激しすぎたので、誤解したのだとおもわれます。 実際には、再送は行われていません。
本日の考察
- TCP クライアントは、ポート番号が使い回しされる。
- Ethernet フレームにTrailer が付加されることがあり、そこには直前の通信の残りが入っている。
- Wireshark の解析機能も万能ではない。
参考文献
Interface (インターフェース) 2008年 09月号 [雑誌]
- 作者:
- 出版社/メーカー: CQ出版
- 発売日: 2008/07/25
- メディア: 雑誌
まあ色々有るのですが、
60bytes未満はショートパケットと呼んで、通常はMAC層でフィルターされてしまいます。なんで60bytes以上なのかは、理由は忘れました。
> TCP クライアントは、ポート番号が使い回しされる。
bindしていなければエフェメラルポートを使うのが普通ですが、一旦コネクションを切断後はポート番号を上げるのが普通です。PCのブラウザのポート番号はコネクションの度に更新されていますよね。
LANでは起こりえないのですが、InternetみたいなWANの場合、むかーしのパケットが遅れてやって来たりするので、それと混同しない為です。
クライアントのプログラムではbindしていないので番号は上げるべきですが、なんで上がらないのだろう?。
> Ethernet フレームにTrailer が付加されることがあり、そこには
> 直前の通信の残りが入っている。
(笑)まあ実装依存?ですね。
> Wireshark の解析機能も万能ではない。
先にも書いたように同一ポート番号であり、もしかしたらシーケンス番号が既出だったのかもしれません。多分こう言ったコメントには何らかの理由があると思った方が良いでしょう。
WireSharkのシーケンス番号の表示の仕方は相対だけなんでしょうか?。絶対で出せないのかなぁ。
#14から#18までの流れもおかしいですよね。
例えば#15と#16は同じクライアントからの送信ですが、#15でFINを送信したにも関わらず、#16ではシーケンス番号が増えていない。
同じ事が#14と#17でも起きていて、#16のシーケンス番号と#17の確認応答番号が不一致となり、それでクライアント側はRSTを発行しているのかもしれません。
また、この辺の不整合を見て、WireSharkはKeep-Aliveだと判断しているのかもしれません。
しかしこう言った事は製品のOS-1では起こっていないんですよね?、多分。
フリー版の制限なのかなぁ。
by hamayan (2008-10-07 00:52)
フレームの長さは、FCS(Frame Check Sequence:いわゆるCRC)の4オクテットを含めて最小64オクテットと定められているそうです。衝突検出をするためには、ある程度の長さが必要という理由だそうで。
#15と#16は、明らかに変です。時間差も9マイクロ秒しか無いので、最小フレーム(64+8 オクテット)の送信に必要な時間を考えると、何も処理していないと思われます。まるで、二つのスレッドが同時にフレームを送信して不整合が起こっているみたいです。
Wiresharkが出してくるシーケンス番号は、相対値なので、絶対値を調べた結果を後ほどまとめます。
参考サイト
http://www.atmarkit.co.jp/fwin2k/network/tcpip007/tcpip03.html
詳説 TCP/IPプロトコル
第7回 イーサネット(その2)――イーサネットのフレーム構造
3.イーサネットのフレーム・フォーマット
by noritan (2008-10-07 08:31)