So-net無料ブログ作成

TCP受信ルーチンにSystemSleepを散りばめる [ColdFire V2]このエントリーを含むはてなブックマーク#

2047506

"masato"さんより、「SystemSleep()を入れたら、うまくいくかも。」というコメントをもらいました。 早速、試してみよう。

SystemSleep()を散りばめた

「ループ内に入れる」という情報しかないので、たくさん入れてしまいました。

        sum = 0;
        for (;;) {
          len = Read(socket,1000);
          if (len < 0) break;
          SystemSleep();
          buf = GetReceiveBuffer(socket,1);
          SystemSleep();
          MemoryFree(buf);
          SystemSleep();
          PrHexWord(len);PrStr(" ");
          SystemSleep();
          sum += len;
        }
        PrHexWord(len);PrStr(" ");PrNum(sum);

これで、オペレーティングシステムの仕事が優先になって、パケットを受信してくれることでしょう。 本当に?

コネクションの確立

パケット・モニタを仕掛けて、実行してみました。 手順は、前回と同じです。

#1	0.000000
	192.168.1.10
	192.168.1.2
	TCP	1024 > 30049 [SYN] Seq=0 Win=1454 Len=0
#2	0.000171
	192.168.1.2
	192.168.1.10
	TCP	30049 > 1024 [SYN, ACK] Seq=0 Ack=1 Win=16616 Len=0 MSS=1460
#3	0.000540
	192.168.1.10
	192.168.1.2
	TCP	1024 > 30049 [ACK] Seq=1 Ack=1 Win=1454 Len=0

コネクションの3ウェイ・ハンドシェイクは、変わりありません。

100バイトの受信

> 100
0064 fffe 100
#4
	4.157214
	192.168.1.10
	192.168.1.2
	TCP	1024 > 30049 [PSH, ACK] Seq=1 Ack=1 Win=1454 Len=4
#5
	4.157724
	192.168.1.2
	192.168.1.10
	TCP	30049 > 1024 [PSH, ACK] Seq=1 Ack=5 Win=16612 Len=100
#6
	4.158245
	192.168.1.10
	192.168.1.2
	TCP	1024 > 30049 [ACK] Seq=5 Ack=101 Win=1454 Len=0

100バイトの受信も問題ありません。

1600バイトの受信

次は、いよいよ、1660バイトの受信です。

> 1600
0218 0218 0210 fffe 1600

1600バイトの受信自体は、できました。 しかし、1セグメント受け取るのに2~3秒かかっているので、表示がポツリポツリ出てきます。 原因をパケット・モニタで調べます。

#7
	17.372065
	192.168.1.10
	192.168.1.2
	TCP	1024 > 30049 [PSH, ACK] Seq=5 Ack=101 Win=1454 Len=5
#8
	17.372573
	192.168.1.2
	192.168.1.10
	TCP	30049 > 1024 [ACK] Seq=101 Ack=10 Win=16607 Len=536
#9
	17.372603
	192.168.1.2
	192.168.1.10
	TCP	30049 > 1024 [ACK] Seq=637 Ack=10 Win=16607 Len=536
#10
	17.373773
	192.168.1.10
	192.168.1.2
	TCP	1024 > 30049 [ACK] Seq=10 Ack=637 Win=1454 Len=0
#11
	17.373857
	192.168.1.2
	192.168.1.10
	TCP	30049 > 1024 [PSH, ACK] Seq=1173 Ack=10 Win=16607 Len=528

クライアントからの要求(#7)に対して、サーバが三つのセグメント(#8, #9, #11)を一度に送ってきて、それに対してクライアントからの返答(#10)が#8に対応するものしか戻ってこない。 ここまでは今までと同じです。

ここで、前回は#9に対する応答をスキップして#11に対する応答を返していました。 ところが、今回は、クライアントからは受信確認が出てきません。

#12
	19.111843
	192.168.1.2
	192.168.1.10
	TCP	[TCP Retransmission] 30049 > 1024 [ACK] Seq=637 Ack=10 Win=16607 Len=536
#13
	19.113008
	192.168.1.10
	192.168.1.2
	TCP	1024 > 30049 [ACK] Seq=10 Ack=1173 Win=1454 Len=0

サーバは、クライアントからの受信確認が届かないので、約2秒後に#12で#9の再送を試みます。 これに対して、クライアントは、#13で受信確認を返します。 この間、1.2ミリ秒です。 PCとの間は、クロスケーブルでつないでいるので、確かにSilentCからの応答です。

#14
	19.113094
	192.168.1.2
	192.168.1.10
	TCP	[TCP Retransmission] 30049 > 1024 [PSH, ACK] Seq=1173 Ack=10 Win=16607 Len=528
#15
	22.934174
	192.168.1.2
	192.168.1.10
	TCP	[TCP Retransmission] 30049 > 1024 [PSH, ACK] Seq=1173 Ack=10 Win=16607 Len=528
#16
	22.935341
	192.168.1.10
	192.168.1.2
	TCP	1024 > 30049 [ACK] Seq=10 Ack=1701 Win=1454 Len=0

受信確認に気を良くしたサーバは、#11の再送として#14を送りますが、やはりクライアントからの受信確認が届きません。 サーバは、約2秒後に再々送として、#15を送り、無事、クライアントから受信確認(#16)を受け取ります。

以上のような状況から、通信が成立した理由は、単にクライアントが受信確認を出すヒマが無かったためと考えられます。 「#12から#13まで」と「#15から#16まで」の処理時間は、どちらもわずかに1.2ミリ秒です。 そのため、到着したセグメントを取り込む前に受信確認パケットを送ってしまったのではないかと推測します。 そして、結果として次のパケットの受信が間に合わなかったのではないでしょうか。

再度、1600バイトの受信

再度、1600バイトの受信を試みます。 さっき、あれだけ再送が必要だったのだから、今度は手加減してくれるかも。

> 1600
0218 0218 0210 fffe 1600

今度も、ちんたらした受信です。

#17
	36.905815
	192.168.1.10
	192.168.1.2
	TCP	1024 > 30049 [PSH, ACK] Seq=10 Ack=1701 Win=1454 Len=5
#18
	36.906323
	192.168.1.2
	192.168.1.10
	TCP	30049 > 1024 [ACK] Seq=1701 Ack=15 Win=16602 Len=536
#19
	36.906357
	192.168.1.2
	192.168.1.10
	TCP	30049 > 1024 [ACK] Seq=2237 Ack=15 Win=16602 Len=536
#20
	36.907498
	192.168.1.10
	192.168.1.2
	TCP	1024 > 30049 [ACK] Seq=15 Ack=2237 Win=1454 Len=0
#21
	36.907557
	192.168.1.2
	192.168.1.10
	TCP	30049 > 1024 [PSH, ACK] Seq=2773 Ack=15 Win=16602 Len=528
#22
	38.323103
	192.168.1.2
	192.168.1.10
	TCP	[TCP Retransmission] 30049 > 1024 [ACK] Seq=2237 Ack=15 Win=16602 Len=536
#23
	38.324225
	192.168.1.10
	192.168.1.2
	TCP	1024 > 30049 [ACK] Seq=15 Ack=2773 Win=1454 Len=0
#24
	38.324298
	192.168.1.2
	192.168.1.10
	TCP	[TCP Retransmission] 30049 > 1024 [PSH, ACK] Seq=2773 Ack=15 Win=16602 Len=528
#25
	41.341667
	192.168.1.2
	192.168.1.10
	TCP	[TCP Retransmission] 30049 > 1024 [PSH, ACK] Seq=2773 Ack=15 Win=16602 Len=528
#26
	41.342827
	192.168.1.10
	192.168.1.2
	TCP	1024 > 30049 [ACK] Seq=15 Ack=3301 Win=1454 Len=0

内容も全く同じです。

1600バイトの受信、三度目

淡い期待を抱いて、三度目の受信に挑戦します。

> 1600
0218 0218 0210 fffe 1600

やっぱり、受信状況は変わらず。

#27
	62.418165
	192.168.1.10
	192.168.1.2
	TCP	1024 > 30049 [PSH, ACK] Seq=15 Ack=3301 Win=1454 Len=5
#28
	62.418663
	192.168.1.2
	192.168.1.10
	TCP	30049 > 1024 [ACK] Seq=3301 Ack=20 Win=16597 Len=536
#29
	62.418699
	192.168.1.2
	192.168.1.10
	TCP	30049 > 1024 [ACK] Seq=3837 Ack=20 Win=16597 Len=536
#30
	62.421627
	192.168.1.10
	192.168.1.2
	TCP	1024 > 30049 [ACK] Seq=20 Ack=3837 Win=1454 Len=0
#31
	62.421704
	192.168.1.2
	192.168.1.10
	TCP	30049 > 1024 [PSH, ACK] Seq=4373 Ack=20 Win=16597 Len=528
#32
	63.571481
	192.168.1.2
	192.168.1.10
	TCP	[TCP Retransmission] 30049 > 1024 [ACK] Seq=3837 Ack=20 Win=16597 Len=536
#33
	63.572649
	192.168.1.10
	192.168.1.2
	TCP	1024 > 30049 [ACK] Seq=20 Ack=4373 Win=1454 Len=0
#34
	63.572737
	192.168.1.2
	192.168.1.10
	TCP	[TCP Retransmission] 30049 > 1024 [PSH, ACK] Seq=4373 Ack=20 Win=16597 Len=528
#35
	65.985579
	192.168.1.2
	192.168.1.10
	TCP	[TCP Retransmission] 30049 > 1024 [PSH, ACK] Seq=4373 Ack=20 Win=16597 Len=528
#36
	65.986743
	192.168.1.10
	192.168.1.2
	TCP	1024 > 30049 [ACK] Seq=20 Ack=4901 Win=1454 Len=0

パケットの内容も全く同じでした。

蛇足:コネクション終了

せっかくだから、コネクションの終了も記録しておきます。

> q
QUIT detected

クライアント側のプログラムは、"q"で始まる行を入力すると、コネクションを切断します。

#37
	79.311295
	192.168.1.10
	192.168.1.2
	TCP	1024 > 30049 [FIN, ACK] Seq=20 Ack=4901 Win=1454 Len=0
#38
	79.311430
	192.168.1.2
	192.168.1.10
	TCP	30049 > 1024 [ACK] Seq=4901 Ack=21 Win=16597 Len=0
#39
	79.311542
	192.168.1.2
	192.168.1.10
	TCP	30049 > 1024 [FIN, ACK] Seq=4901 Ack=21 Win=16597 Len=0
#40
	79.311888
	192.168.1.10
	192.168.1.2
	TCP	1024 > 30049 [ACK] Seq=21 Ack=4902 Win=1454 Len=0

これは、問題なさそうです。

本日の考察

以上の実験から、以下の事柄が判明しました。

  1. SilentCは、受信したセグメントの処理が終わらないうちに、早々に受信確認を送っているように見える。結果として、さっさと次のセグメントが送られてきてしまい、受信に失敗しているので、自分で自分の首を絞めているのではないか。
  2. PCは、再送が必要になるような信頼性の低い通信路であることを認識すべきなのに、手加減せずにパケットを送りつけてくる。これは、クライアントが常に主張している1454バイトのウィンドウ・サイズと関連するのかもしれない。

参考文献

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

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

  • 作者:
  • 出版社/メーカー: CQ出版
  • 発売日: 2008/07/25
  • メディア: 雑誌
ネットワークマガジン 2004年2月号
特集1 徹底図解 はじめての「プロトコル」

「過去記事PDF」が入っている付録CD-ROMを引っ張り出してきました。


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

nice! 0

コメント 3

hamayan

> 手加減せずにパケットを送りつけてくる。

(笑)手加減しません、全然しません。ネットワークOSと言っている以上アプリケーションとソケットは別プロセスで動いていて、諦めている様に見えても実はコネクションが切断されていない以上諦めていなかったりして、忘れた頃に送って来たりもします。

勿論リトライの上限は有ると思いますが。

> クライアントが常に主張している1454バイトのウィンドウ・サイズと

まあ相手側が、「もうこれ以上送らないでくれ」とは言っておらず、「まだまだ全然平気」と言っているので、送るべきデータが有るから送っている!と、PC側は全然悪くないですね。
送信側は輻輳制御は行いますが、それ以外は確認応答を受けるまでは基本的にリトライ!リトライです。

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

noritan

ふくそう制御というのは、状況に応じて手加減して送信する事かと思っていました。あるいは、これ以上は無いくらい、すでに手加減した状態で送信しているのでしょうか。

一番の問題は、やはりコンスタントなウィンドウ・サイズかな?

by noritan (2008-09-25 22:30) 

hamayan

> 状況に応じて手加減して送信する事かと思っていました

スライディングWindowで送信する時、最初は少数のパケットを送って確認応答を待って、それで問題無ければ今度はもっと増やして、それでも問題無ければ更に増やして、でもいつかはいっぱいいっぱいになって再送を掛ける必要が発生します。再送が沢山必要になった状態を輻輳と呼んでいますので、送信側は輻輳を発生させない様にスライディングWindowを調整します。

しかし1個や2個位のパケット数でもう再送が必要になっている状態では、これ以上の手加減は出来ないでしょう。

なんでWinodwサイズが変化しないか全く不思議です。物の本に拠れば、まあ「詳解TCP/IP」だったりするのですが、「多少受信バッファが減ったとしてもあまり小さなWinodwサイズを報告するな!、細かいパケットが増えてトラフィックが増大するから。」とは書かれていますが、この本はBSD等のUNIX環境での実装を書いた本なのでそれはCPUパワーもメモリも豊富な環境下での話であり、組み込みマイコンには辛い話です。

実際にはWindows辺りでも受信直後ではWindowサイズが減っていたりしますので、組み込みでこれが変化しないなど、どんなバッファ構造になっているのか想像もできません。

1454bytesと言う中途半端なサイズもなんでかなぁ?。
by hamayan (2008-09-25 23:03) 

コメントを書く

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

トラックバック 0

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

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