したらばワシもSilentCでTCPをやってみよう [SilentC]
noritanさんの所でSilentCの関数リストを作成されて居られたのでフムフムと参考にさせて頂いたのですが、その中の”GetDefMtu”と言う関数に目が止まりました。今までUDPしか使っていなかったので、こんな関数が有る事に全然気付きませんでした。
”Mtu”って、あのMTUですよね。通常MTUはIPデータグラム全体の最大長を言っており、MSSとはMTU-40=MSSの関係が有ります。※MSSは最大長なので、実際のデータ長はもっと小さくなる可能性が有りますが。
”GetDefMtu”の2番目の引数に0を入れて置くと、戻り値として現在のMTUを返してくれる様です。そこでそれを表示させてみると、
576ではなく1454と言う数字が返って来ました。これって、、、1454と言う数字の出自ですが、もしかしてフレッツADSLのMTU?。(※576と言うのは、オプションの交換をしない為に、PC側はデフォルトのMSS値である536を使うから、それを見越しているのではと期待していた。)
ではMTUを変化させてみると、
今度は1500に設定できました。1500はEthernet上の最大です。
前にSilentCはコネクション時にオプションの交換を行っていないと書いた訳ですが、
http://hamayan.blog.so-net.ne.jp/2008-09-14-2
もしかしてこれでオプションを交換するようになるか!と思い、物凄く出鱈目なhttpサーバーを書いてみました。
ポート番号は8080にしています。内部でループしていないので、一回リクエストに答えたら終了します。
結果は、ブラウザ上には無事”hello world”が表示されました。パケットをキャプチャしてみるとコネクションの所は、(※全部掲載すると長いので、TCPヘッダーとデータの一部のみ)
あれ!、やっぱりオプションは交換しないみたいです。それより何故かWindowサイズを1500と答えています???。
続いてESTABLISHED中と後、
無事データの交換からコネクションの切断まで完了しています。ブラウザからは394bytesのデータが送られているのですが、SilentC側の受信バッファはその程度のデータにはものともせず、やっぱりWindowサイズが1500bytesです。
ちなみにPC側は11bytes受けたら、きっちりその分減っています。
実はこの後httpサーバーアプリケーションは停止しているにも関わらず、すっかり忘れてブラウザのリロードボタンを押してしまいました。まあコネクションを受け付けない状態になっている筈なので、こう言った場合はRSTを返してくるでしょう、、、あれ???。
コネクション受け付けちゃったので、ブラウザはリクエストを発行し、SilentCはちゃんと受け取ってしまいました、、。
ですが、その後当然ながら”hello World”は返して来ません。
ブラウザはずっと受信待ち状態になっているので、仕方が無いのでバツボタンでコネクション切断です。
切断はできるようです。
さて、問題はここから。
再度telnetでhttp出鱈目サーバーを起動し、ブラウザからアクセスしてみます。
もう、こちらからの接続要求には全く応じません。RSTも来ません。ブラウザは接続を諦めてしまいました。
”Mtu”って、あのMTUですよね。通常MTUはIPデータグラム全体の最大長を言っており、MSSとはMTU-40=MSSの関係が有ります。※MSSは最大長なので、実際のデータ長はもっと小さくなる可能性が有りますが。
”GetDefMtu”の2番目の引数に0を入れて置くと、戻り値として現在のMTUを返してくれる様です。そこでそれを表示させてみると、
DisMTU() { char soc = CreateSocket( 1 ); int mtu = GetDefMtu( soc, 0 ); PrNum( mtu ); CloseSocket( soc ); }
576ではなく1454と言う数字が返って来ました。これって、、、1454と言う数字の出自ですが、もしかしてフレッツADSLのMTU?。(※576と言うのは、オプションの交換をしない為に、PC側はデフォルトのMSS値である536を使うから、それを見越しているのではと期待していた。)
ではMTUを変化させてみると、
SetMTU() { char soc = CreateSocket( 1 ); GetDefMtu( soc, 1500 ); int mtu = GetDefMtu( soc, 0 ); PrNum( mtu ); CloseSocket( soc ); }
今度は1500に設定できました。1500はEthernet上の最大です。
前にSilentCはコネクション時にオプションの交換を行っていないと書いた訳ですが、
http://hamayan.blog.so-net.ne.jp/2008-09-14-2
もしかしてこれでオプションを交換するようになるか!と思い、物凄く出鱈目なhttpサーバーを書いてみました。
ポート番号は8080にしています。内部でループしていないので、一回リクエストに答えたら終了します。
http() { char soc = CreateSocket( 1 ); char newsoc,*buf; int len; GetDefMtu( soc, 1500 ); Bind( soc, 8080, 1 ); newsoc = Accept( soc, -1 ); len = Read( newsoc, -1 ); if( len == -3 ) { PrStr( "closed by other.\r\n" ); CloseSocket( newsoc ); return; } buf = GetReceiveBuffer( newsoc, 1 ); if( StrStr( buf, "GET /" ) != 0 ) { PrStr( "get /.\r\n" ); Write( newsoc, "hello world", 11 ); WaitWriteComplete( newsoc ); } MemoryFree( buf ); CloseSocket( newsoc ); }
結果は、ブラウザ上には無事”hello world”が表示されました。パケットをキャプチャしてみるとコネクションの所は、(※全部掲載すると長いので、TCPヘッダーとデータの一部のみ)
<< パケット 1 >> --- TCPヘッダ [1546] → [8080] ---------------------------------------------- 【発信元ポート番号】1546 【発信先ポート番号】8080 【シーケンス番号】906374635 (0x36062DEB) 【応答番号】0 (0x00000000) 【データオフセット】28 (0x1C) 【フラグ】【URG】0【ACK】0【PSH】0【RST】0【SYN】1【FIN】0 【ウィンドウ】16384 (0x4000) バイト 【チェックサム】0x35BF (OK) 【緊急ポインタ】0 【オプション】 【オプション番号】2 (最大受信セグメント長 = 1460 (0x05B4) バイト) 【オプション番号】1 (オペレーションなし) 【オプション番号】1 (オペレーションなし) 【オプション番号】4 (SACK許可) << パケット 2 >> --- TCPヘッダ [8080] → [1546] ---------------------------------------------- 【発信元ポート番号】8080 【発信先ポート番号】1546 【シーケンス番号】441227935 (0x1A4C9A9F) 【応答番号】906374636 (0x36062DEC) 【データオフセット】20 (0x14) 【フラグ】【URG】0【ACK】1【PSH】0【RST】0【SYN】1【FIN】0 【ウィンドウ】1500 (0x05DC) バイト 【チェックサム】0xE7A9 (OK) 【緊急ポインタ】0 【オプション】なし << パケット 3 >> --- TCPヘッダ [1546] → [8080] ---------------------------------------------- 【発信元ポート番号】1546 【発信先ポート番号】8080 【シーケンス番号】906374636 (0x36062DEC) 【応答番号】441227936 (0x1A4C9AA0) 【データオフセット】20 (0x14) 【フラグ】【URG】0【ACK】1【PSH】0【RST】0【SYN】0【FIN】0 【ウィンドウ】16616 (0x40E8) バイト 【チェックサム】0xAC9E (OK) 【緊急ポインタ】0 【オプション】なし
あれ!、やっぱりオプションは交換しないみたいです。それより何故かWindowサイズを1500と答えています???。
続いてESTABLISHED中と後、
<< パケット 4 >> --- TCPヘッダ [1546] → [8080] ---------------------------------------------- 【発信元ポート番号】1546 【発信先ポート番号】8080 【シーケンス番号】906374636 (0x36062DEC) 【応答番号】441227936 (0x1A4C9AA0) 【データオフセット】20 (0x14) 【フラグ】【URG】0【ACK】1【PSH】1【RST】0【SYN】0【FIN】0 【ウィンドウ】16616 (0x40E8) バイト 【チェックサム】0x4BEE (OK) 【緊急ポインタ】0 【オプション】なし ---【データ】394 (0x018A) バイト --------------------------------- SJIS ------- 0000 47 45 54 20 2F 20 48 54 - 54 50 2F 31 2E 31 0D 0A GET / HTTP/1.1.. 0010 55 73 65 72 2D 41 67 65 - 6E 74 3A 20 4F 70 65 72 User-Agent: Oper << パケット 5 >> --- TCPヘッダ [8080] → [1546] ---------------------------------------------- 【発信元ポート番号】8080 【発信先ポート番号】1546 【シーケンス番号】441227936 (0x1A4C9AA0) 【応答番号】906375030 (0x36062F76) 【データオフセット】20 (0x14) 【フラグ】【URG】0【ACK】1【PSH】0【RST】0【SYN】0【FIN】0 【ウィンドウ】1500 (0x05DC) バイト 【チェックサム】0xE620 (OK) 【緊急ポインタ】0 【オプション】なし << パケット 6 >> --- TCPヘッダ [8080] → [1546] ---------------------------------------------- 【発信元ポート番号】8080 【発信先ポート番号】1546 【シーケンス番号】441227936 (0x1A4C9AA0) 【応答番号】906375030 (0x36062F76) 【データオフセット】20 (0x14) 【フラグ】【URG】0【ACK】1【PSH】1【RST】0【SYN】0【FIN】0 【ウィンドウ】1500 (0x05DC) バイト 【チェックサム】0x543F (OK) 【緊急ポインタ】0 【オプション】なし ---【データ】11 (0x000B) バイト ---------------------------------- SJIS ------- 0000 68 65 6C 6C 6F 20 77 6F - 72 6C 64 hello world << パケット 7 >> --- TCPヘッダ [1546] → [8080] ---------------------------------------------- 【発信元ポート番号】1546 【発信先ポート番号】8080 【シーケンス番号】906375030 (0x36062F76) 【応答番号】441227947 (0x1A4C9AAB) 【データオフセット】20 (0x14) 【フラグ】【URG】0【ACK】1【PSH】0【RST】0【SYN】0【FIN】0 【ウィンドウ】16605 (0x40DD) バイト 【チェックサム】0xAB14 (OK) 【緊急ポインタ】0 【オプション】なし << パケット 8 >> --- TCPヘッダ [8080] → [1546] ---------------------------------------------- 【発信元ポート番号】8080 【発信先ポート番号】1546 【シーケンス番号】441227947 (0x1A4C9AAB) 【応答番号】906375030 (0x36062F76) 【データオフセット】20 (0x14) 【フラグ】【URG】0【ACK】1【PSH】0【RST】0【SYN】0【FIN】1 【ウィンドウ】1500 (0x05DC) バイト 【チェックサム】0xE614 (OK) 【緊急ポインタ】0 【オプション】なし << パケット 9 >> --- TCPヘッダ [1546] → [8080] ---------------------------------------------- 【発信元ポート番号】1546 【発信先ポート番号】8080 【シーケンス番号】906375030 (0x36062F76) 【応答番号】441227948 (0x1A4C9AAC) 【データオフセット】20 (0x14) 【フラグ】【URG】0【ACK】1【PSH】0【RST】0【SYN】0【FIN】0 【ウィンドウ】16605 (0x40DD) バイト 【チェックサム】0xAB13 (OK) 【緊急ポインタ】0 【オプション】なし << パケット 10 >> --- TCPヘッダ [1546] → [8080] ---------------------------------------------- 【発信元ポート番号】1546 【発信先ポート番号】8080 【シーケンス番号】906375030 (0x36062F76) 【応答番号】441227948 (0x1A4C9AAC) 【データオフセット】20 (0x14) 【フラグ】【URG】0【ACK】1【PSH】0【RST】0【SYN】0【FIN】1 【ウィンドウ】16605 (0x40DD) バイト 【チェックサム】0xAB12 (OK) 【緊急ポインタ】0 【オプション】なし << パケット 11 >> --- TCPヘッダ [8080] → [1546] ---------------------------------------------- 【発信元ポート番号】8080 【発信先ポート番号】1546 【シーケンス番号】441227948 (0x1A4C9AAC) 【応答番号】906375031 (0x36062F77) 【データオフセット】20 (0x14) 【フラグ】【URG】0【ACK】1【PSH】0【RST】0【SYN】0【FIN】0 【ウィンドウ】1500 (0x05DC) バイト 【チェックサム】0xE613 (OK) 【緊急ポインタ】0 【オプション】なし
無事データの交換からコネクションの切断まで完了しています。ブラウザからは394bytesのデータが送られているのですが、SilentC側の受信バッファはその程度のデータにはものともせず、やっぱりWindowサイズが1500bytesです。
ちなみにPC側は11bytes受けたら、きっちりその分減っています。
実はこの後httpサーバーアプリケーションは停止しているにも関わらず、すっかり忘れてブラウザのリロードボタンを押してしまいました。まあコネクションを受け付けない状態になっている筈なので、こう言った場合はRSTを返してくるでしょう、、、あれ???。
<< パケット 12 >> --- TCPヘッダ [1547] → [8080] ---------------------------------------------- 【発信元ポート番号】1547 【発信先ポート番号】8080 【シーケンス番号】3462719371 (0xCE64E78B) 【応答番号】0 (0x00000000) 【データオフセット】28 (0x1C) 【フラグ】【URG】0【ACK】0【PSH】0【RST】0【SYN】1【FIN】0 【ウィンドウ】16384 (0x4000) バイト 【チェックサム】0xE3BE (OK) 【緊急ポインタ】0 【オプション】 【オプション番号】2 (最大受信セグメント長 = 1460 (0x05B4) バイト) 【オプション番号】1 (オペレーションなし) 【オプション番号】1 (オペレーションなし) 【オプション番号】4 (SACK許可) << パケット 13 >> --- TCPヘッダ [8080] → [1547] ---------------------------------------------- 【発信元ポート番号】8080 【発信先ポート番号】1547 【シーケンス番号】484341600 (0x1CDE7760) 【応答番号】3462719372 (0xCE64E78C) 【データオフセット】20 (0x14) 【フラグ】【URG】0【ACK】1【PSH】0【RST】0【SYN】1【FIN】0 【ウィンドウ】1500 (0x05DC) バイト 【チェックサム】0xB656 (OK) 【緊急ポインタ】0 【オプション】なし << パケット 14 >> --- TCPヘッダ [1547] → [8080] ---------------------------------------------- 【発信元ポート番号】1547 【発信先ポート番号】8080 【シーケンス番号】3462719372 (0xCE64E78C) 【応答番号】484341601 (0x1CDE7761) 【データオフセット】20 (0x14) 【フラグ】【URG】0【ACK】1【PSH】0【RST】0【SYN】0【FIN】0 【ウィンドウ】16616 (0x40E8) バイト 【チェックサム】0x7B4B (OK) 【緊急ポインタ】0 【オプション】なし << パケット 15 >> --- TCPヘッダ [1547] → [8080] ---------------------------------------------- 【発信元ポート番号】1547 【発信先ポート番号】8080 【シーケンス番号】3462719372 (0xCE64E78C) 【応答番号】484341601 (0x1CDE7761) 【データオフセット】20 (0x14) 【フラグ】【URG】0【ACK】1【PSH】1【RST】0【SYN】0【FIN】0 【ウィンドウ】16616 (0x40E8) バイト 【チェックサム】0xC99A (OK) 【緊急ポインタ】0 【オプション】なし ---【データ】437 (0x01B5) バイト --------------------------------- SJIS ------- 0000 47 45 54 20 2F 20 48 54 - 54 50 2F 31 2E 31 0D 0A GET / HTTP/1.1.. 0010 55 73 65 72 2D 41 67 65 - 6E 74 3A 20 4F 70 65 72 User-Agent: Oper << パケット 16 >> --- TCPヘッダ [8080] → [1547] ---------------------------------------------- 【発信元ポート番号】8080 【発信先ポート番号】1547 【シーケンス番号】484341601 (0x1CDE7761) 【応答番号】3462719809 (0xCE64E941) 【データオフセット】20 (0x14) 【フラグ】【URG】0【ACK】1【PSH】0【RST】0【SYN】0【FIN】0 【ウィンドウ】1500 (0x05DC) バイト 【チェックサム】0xB4A2 (OK) 【緊急ポインタ】0 【オプション】なし
コネクション受け付けちゃったので、ブラウザはリクエストを発行し、SilentCはちゃんと受け取ってしまいました、、。
ですが、その後当然ながら”hello World”は返して来ません。
ブラウザはずっと受信待ち状態になっているので、仕方が無いのでバツボタンでコネクション切断です。
<< パケット 17 >> --- TCPヘッダ [1547] → [8080] ---------------------------------------------- 【発信元ポート番号】1547 【発信先ポート番号】8080 【シーケンス番号】3462719809 (0xCE64E941) 【応答番号】484341601 (0x1CDE7761) 【データオフセット】20 (0x14) 【フラグ】【URG】0【ACK】1【PSH】0【RST】0【SYN】0【FIN】1 【ウィンドウ】16616 (0x40E8) バイト 【チェックサム】0x7995 (OK) 【緊急ポインタ】0 【オプション】なし << パケット 18 >> --- TCPヘッダ [8080] → [1547] ---------------------------------------------- 【発信元ポート番号】8080 【発信先ポート番号】1547 【シーケンス番号】484341601 (0x1CDE7761) 【応答番号】3462719810 (0xCE64E942) 【データオフセット】20 (0x14) 【フラグ】【URG】0【ACK】1【PSH】0【RST】0【SYN】0【FIN】0 【ウィンドウ】1500 (0x05DC) バイト 【チェックサム】0xB4A1 (OK) 【緊急ポインタ】0 【オプション】なし << パケット 19 >> --- TCPヘッダ [8080] → [1547] ---------------------------------------------- 【発信元ポート番号】8080 【発信先ポート番号】1547 【シーケンス番号】484341601 (0x1CDE7761) 【応答番号】3462719810 (0xCE64E942) 【データオフセット】20 (0x14) 【フラグ】【URG】0【ACK】1【PSH】0【RST】0【SYN】0【FIN】1 【ウィンドウ】1500 (0x05DC) バイト 【チェックサム】0xB4A0 (OK) 【緊急ポインタ】0 【オプション】なし << パケット 20 >> --- TCPヘッダ [1547] → [8080] ---------------------------------------------- 【発信元ポート番号】1547 【発信先ポート番号】8080 【シーケンス番号】3462719810 (0xCE64E942) 【応答番号】484341602 (0x1CDE7762) 【データオフセット】20 (0x14) 【フラグ】【URG】0【ACK】1【PSH】0【RST】0【SYN】0【FIN】0 【ウィンドウ】16616 (0x40E8) バイト 【チェックサム】0x7994 (OK) 【緊急ポインタ】0 【オプション】なし
切断はできるようです。
さて、問題はここから。
再度telnetでhttp出鱈目サーバーを起動し、ブラウザからアクセスしてみます。
<< パケット 3 >> --- TCPヘッダ [1555] → [8080] ---------------------------------------------- 【発信元ポート番号】1555 【発信先ポート番号】8080 【シーケンス番号】2268105760 (0x87308C20) 【応答番号】0 (0x00000000) 【データオフセット】28 (0x1C) 【フラグ】【URG】0【ACK】0【PSH】0【RST】0【SYN】1【FIN】0 【ウィンドウ】16384 (0x4000) バイト 【チェックサム】0x8656 (OK) 【緊急ポインタ】0 【オプション】 【オプション番号】2 (最大受信セグメント長 = 1460 (0x05B4) バイト) 【オプション番号】1 (オペレーションなし) 【オプション番号】1 (オペレーションなし) 【オプション番号】4 (SACK許可) << パケット 4 >> --- TCPヘッダ [1555] → [8080] ---------------------------------------------- 【発信元ポート番号】1555 【発信先ポート番号】8080 【シーケンス番号】2268105760 (0x87308C20) 【応答番号】0 (0x00000000) 【データオフセット】28 (0x1C) 【フラグ】【URG】0【ACK】0【PSH】0【RST】0【SYN】1【FIN】0 【ウィンドウ】16384 (0x4000) バイト 【チェックサム】0x8656 (OK) 【緊急ポインタ】0 【オプション】 【オプション番号】2 (最大受信セグメント長 = 1460 (0x05B4) バイト) 【オプション番号】1 (オペレーションなし) 【オプション番号】1 (オペレーションなし) 【オプション番号】4 (SACK許可) << パケット 5 >> --- TCPヘッダ [1555] → [8080] ---------------------------------------------- 【発信元ポート番号】1555 【発信先ポート番号】8080 【シーケンス番号】2268105760 (0x87308C20) 【応答番号】0 (0x00000000) 【データオフセット】28 (0x1C) 【フラグ】【URG】0【ACK】0【PSH】0【RST】0【SYN】1【FIN】0 【ウィンドウ】16384 (0x4000) バイト 【チェックサム】0x8656 (OK) 【緊急ポインタ】0 【オプション】 【オプション番号】2 (最大受信セグメント長 = 1460 (0x05B4) バイト) 【オプション番号】1 (オペレーションなし) 【オプション番号】1 (オペレーションなし) 【オプション番号】4 (SACK許可)
もう、こちらからの接続要求には全く応じません。RSTも来ません。ブラウザは接続を諦めてしまいました。
2008-09-28 02:41
nice!(0)
コメント(2)
トラックバック(0)
サーバ・アプリケーションが停止している時にコネクションを受け付けちゃったのは、"newsoc"はクローズされたけど"soc"がクローズされていなかったためだと思います。このとき、"soc"は、オペレーティング・システムが管理していて、アプリケーションに構わずリクエストを受け付けます。
再度、サーバを起動したら、すでに動作しているソケットと競合することになるので、「ソケット売り切れ」または「ポート番号売り切れ」という事態が考えられます。
by noritan (2008-09-29 13:45)
ああナルホド。
”newsoc”はOS-1プログラミングマニュアルの”Accept”のサンプルを見て使っているのですが、終了時には”soc”もCLOSEしないといけないのね。
この”soc”と”newsoc”の使い分けがイマイチ判らなかったのだけれど、これで判りました。
by hamayan (2008-09-29 14:05)