So-net無料ブログ作成
検索選択

付録基板同士のクライアント・サーバ・システム [ColdFire V2]このエントリーを含むはてなブックマーク#

2047506

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"まで発行されているし。 想像するに、ポート番号の使い回しが激しすぎたので、誤解したのだとおもわれます。 実際には、再送は行われていません。

本日の考察

  1. TCP クライアントは、ポート番号が使い回しされる。
  2. Ethernet フレームにTrailer が付加されることがあり、そこには直前の通信の残りが入っている。
  3. Wireshark の解析機能も万能ではない。

参考文献


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

nice! 0

コメント 2

hamayan

まあ色々有るのですが、
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) 

noritan

フレームの長さは、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) 

コメントを書く

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

トラックバック 0

この記事のトラックバックURL:
※言及リンクのないトラックバックは受信されません。