660 likes | 816 Views
情報処理 IIb(n) vol.4. 慶應義塾大学 村井純. 今日やること. コンピュータコミュニケーションの基礎 トランスポート層 TCP 輻輳制御 状態遷移 コンピュータコミュニケーションのモデル プロキシサーバ. TCP (Transmission Control Protocol). 異なるホストのアプリケーション間に信頼性のある通信を提供 IP が提供する通信に信頼性を与える コネクション型通信 タイムアウトと再転送 エラー検出とエラー訂正 フロー制御 順序の再構成
E N D
情報処理IIb(n) vol.4 慶應義塾大学 村井純
今日やること • コンピュータコミュニケーションの基礎 • トランスポート層 • TCP • 輻輳制御 • 状態遷移 • コンピュータコミュニケーションのモデル • プロキシサーバ
TCP (Transmission Control Protocol) • 異なるホストのアプリケーション間に信頼性のある通信を提供 • IPが提供する通信に信頼性を与える • コネクション型通信 • タイムアウトと再転送 • エラー検出とエラー訂正 • フロー制御 • 順序の再構成 • データ転送に関するインターフェイスを提供
輻輳制御ルール1:返事で判断 返事が早い→空いてる→沢山送る
輻輳制御ルール2:混んだら小さく 返事が遅い→混んでる→少し送る
輻輳制御ルール2:混んだら小さく 返事が無い→混んでる→少し送る
輻輳制御ルール3:空いても急ぐな 返事が早い→空いてる→ゆっくり送る
サンフランシスコ→藤沢(バイト数/日)TCP/UDP利用別グラフサンフランシスコ→藤沢(バイト数/日)TCP/UDP利用別グラフ
インターネットが遅い原因 • 返事が遅い • 相手がのろい • 途中がのろい • 相手がのろい場合 • 相手が過負荷 • アクセスが多すぎる • 力がなさ過ぎる • 途中がのろい場合 • どこかの待ち行列であふれている
Src Port Dst Port Sequence Number Acknowledgement Number Window Size Offset Flag Reserved Urgent Pointer TCP Checksum Padding Option (if any) Data TCPヘッダ
Flags CODE • FlagsはTCPがコネクション制御に利用 • 6つのflagsがある • URG: Urgent Pointerが有効 • ACK: Acknowledge Numberが有効 • PSH: セグメントをすぐに上位層へ渡す • RST: エラーによる強制的なクローズ • SYN: コネクションセットアップの同期をとる • FIN: コネクションを終了する
TCPの状態遷移図 CLOSED Passive Open Recv SYN send SYN,ACK Active Open LISTEN Send SYN Send SYN SYN_RCVD Recv RST SYN_SENT Recv SYN Recv SYN,ACK Send ACK Recv ACK ESTABLISHED データ転送状態
SEQ=812 SYN SEQ=812 No ACK SEQ=123 SYN SEQ=123 ACK=813 SEQ=123 SEQ=813 ACK=124 SEQ=813 ACK=124 ACK=813 コネクション開始 送信側 受信側
3way Handshake • コネクションのセットアップ • ホスト1、ホスト2間で • ホスト1はSYN Flagのついたパケットを送る • SYNを受けたホスト2は相手との同期を取るためにSYN Flagのついたパケットを送る。この時いっしょに受け取ったSYNに対するACK Flagもつける • ホスト2からSYNを受け取ったホスト1はACKを返す • SYN と ACKが相乗り • Piggy Back
FIN SEQ=457 SEQ=789 ACK=458 FIN SEQ=790 SEQ=458 ACK=791 コネクションの終了 • コネクションの終了はFINFlagによって行う • コネクションの終了は一方ごとにできる
SEQ=231 DATA SEQ=456 ACK=232 SEQ=232 DATA データの転送 • Sequence NumberとAcknowledge Numberによって、データの順番を保証 • ACKが帰ってきたら次のデータを送る • ACKが帰ってこなかったら前のデータを再転送
データ転送(続き) • いちいちACKを待つと効率が悪い • 推測を元にある程度先送りした方が良い • だが、先送りしすぎるとACKがなかなか帰ってこない • 幅を決めなければならない
Congestion Control(輻輳制御) • 輻輳(Congestion) • 中間ノードがデータグラムの過負荷によって遅延が生じた状態. • 輻輳を解決するために • ウィンドウコントロール • スロースタート
ウィンドウコントロール • 先送りする幅を決める仕組み • 送るパケットを制限する SEQ=238 SEQ=270 4 SEQ=302 4 SEQ=334 ACK=335
スロー・スタート • ネットワークに送り出されるパケットの通信速度を他方のエンドから返される確認応答の通信速度を同期させるためのもの • 送り手のTCPに別のウィンドウを追加する。 • 輻輳ウィンドウ(cwnd) • 新しいコネクションが確立 • 輻輳ウィンドウをセグメント1つに初期化 • ACKが受け取られるたびに、輻輳ウィンドウは1セグメントずつ増やされる
スロー・スタート animation 1:513(512)ack1, win4096 cwnd=1 1 ack513, win8192 2 cwnd=2 513:1025(512)ack1, win4096 3 1025:1537(512)ack1, win4096 4 ack1025, win8192 5 cwnd=3 1537:2049(512)ack1, win4096 6 2049:2561(512)ack1, win4096 7 ack1537, win8192 8 cwnd=4 ack2049, win8192 9 cwnd=5
まとめ • IPはホストホスト間の通信まで • トランスポート層はポート番号によってアプリケーション間の通信を実現 • UDPはIPの提供する品質の通信 • TCPはいろいろな機能を用いることで、信頼性のある通信をアプリケーション間に提供する
代返 • 代わりに返事をすること • 授業 • 当事者がその場に入れないときに使う • クライアントがサーバに到達する代わり代理に問い合わせる • Proxy Server
Proxy Server • 本物のサービスの代わりに、その代理のサービスを提供するサーバ • WWW Proxy Server Proxy Client 本物server
Socket Programming2 慶応大学政策・メディア研究科 今泉英明 hiddy@sfc.wide.ad.jp
先週の課題について • 20人が提出(49%) • 締め切りを過ぎても再提出可能(出さないよりは大幅にまし) • 寄せられた問題点 • バッファサイズが1024bytesじゃ少ない。 • void main()はint main()にした方がいい。 • サーバのソケットから読み出すべき。 • 他人の名前でメールできてしまう。 • Subjectが書けるようになっていない
課題の問題点 • サーバとうまくおしゃべりできていない • Read()がブロックしてしまう • サーバからしゃべりだしたりしたとき • サーバから1024bytes以上送られてきたとき • 以下の順序に依存しているのが原因 • 標準入力から読む(read()でブロック) • ソケットに書き込む • ソケットから読む(read()でブロック) • 標準出力に書き込む
今日の目標 • ソケットプログラミングの基礎を学ぶ • 非同期多重入出力 • 複数のディスクリプタを同時に利用しよう! • TCPを用いるサーバを書こう • UNIXプログラミングの勉強 • プロセスについて
非同期多重入出力 • 読み込み準備の整ったファイルディスクリプタから読みたい • 書き込み準備の整ったファイルディスクリプタから読みたい • とにかく複数のディスクリプタから非同期に読み書きがしたい
Select()システムコール • 複数のディスクリプタを非同期に扱うためのシステムコール • ファイルディスクリプタの集合を渡して、読み出し/書き込み可能なファイルディスクリプタを教えてもらう • 一定のタイムアウトも指定できる
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 fd_set • ファイルディスクリプタの集合を保持するためのビットマップ • 以下のような10bitのビットマップにファイルディスクリプタ0, 4, 8という集合を表現させると 10bitsあると10個のファイルディスクリプタの集合を 表現することができる • FreeBSD3.2, Solaris 2.6では1024bits • BSD/OS3.1では256bits
fd_setの操作 • 以下のインターフェースを利用して操作 fd_set fds; FD_ZERO(&fds); /*fdsをゼロに初期化*/ FD_SET(fd, &fds);/*fdsにfd番目のビットを上げる*/ FD_ISSET(fd, &fds);/*fdsにfd番目のビットが設定さ れているか調べる*/ FD_CLR(fd, &fds);/*fdsからfd番目のbitを落とす*/
Select() システムコール int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); • Selectは0から(nfds-1)までのファイルディスクリプタを検査 • 最大の値のディスクリプタ+1を設定 • ・readfds/writefdsは読み込み/書き込み可能かを調べたい • ディスクリプタの集合 • ・exceptfdsはとりあえず気にしない(詳しくは参考文献21章) • ・どれかのディスクリプタが読み/書き可能になった場合に戻る • ・それ以外はタイマの指定に依存 • ・timeoutはタイムアウトするまでのタイマ値(秒、マイクロ秒)
long tv_sec long tv_usec timeval構造体 • longである秒とマイクロ秒の二つのメンバ • タイマの値を指定できる
タイマの設定 • Selectは以下の3つのタイマの指定が可能 • NULLを指定 - タイムアウトしない • Timeval構造体を指定 - 指定された一定時間でタイムアウト • 0秒0マイクロ秒に指定したTimeval構造体を指定 - ファイルディスクリプタを検査した後すぐにタイムアウト
1 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 Selectの手順1 ファイルディスクリプタ0, 4, 8という集合のうちどれが読み出し可能になったか知りたい場合 • int nfds; • fd_set fds; • FD_ZERO(&fds); • FD_SET(0, &fds); • FD_SET(4, &fds); • FD_SET(8, &fds); • nfds = 8 + 1; • select(nfds, &fds, NULL, NULL, NULL); • 読み出し可能なビットが立つ 0 1 2 3 4 5 6 7 8 9
1 0 0 0 0 0 0 0 1 0 Selectの手順2 Selectを実行した後の処理 • select(nfds, &fds, NULL, NULL, NULL); • 読み出し可能なビットが立つ if(FD_ISSET(0, &fds){ read(0, ….); } if(FD_ISSET(4, &fds){ read(4, ….); } if(FD_ISSET(8, &fds){ read(8, …..); } 0 1 2 3 4 5 6 7 8 9
実際に変更 • 別紙参照
ここまでのまとめ • ネットワークプログラミングをするにはソケットを利用する • ネットワークに流す複数バイトのデータ型(long,short)はネットワークバイトオーダにあわせる • TCPでコネクションを張るとbyte streamが流れるパイプが張れる • その上に流れるデータはそれを扱うプログラムが担う • 複数のディスクリプタを非同期に扱うにはselect()システムコールを利用する。
サーバ側を書いてみよう • サーバも同様にTCPのコネクションが張れると単なるディスクリプタ • 違いは、みんなからの接続要求を待つためのソケットを開く • 自分でソケットにアドレスをbindしなければならない • 接続要求を保持するためにListen()を呼んでキューを作成 • 要求にaccept()して、それぞれのクライアントと通信するためのディスクリプタを作成 • あとは一緒
システムコールの流れ TCPサーバ TCPクライアント socket() socket() bind() connect() listen() accept() write() read() read() write() read() EOFの通知 close() close()
プログラムの流れ • Sockaddr_in構造体に、サーバのIPアドレスとポート番号を設定 • ソケットを開く • 1のアドレスをソケットにbind() • Listen(接続待機)でキューを作成 • 接続要求に対してAccept() • おしゃべり • Close()
初期状態 クライアント プロセス サーバ プロセス Port A Port B Port C ホストA ホストB IP Address: xx.xx.xx.xx. IP Address: xx.xx.xx.xx.
Socketを開いた状態 Socketを開く クライアント プロセス サーバ プロセス Port A Port B Port C ホストA ホストB IP Address: xx.xx.xx.xx. IP Address: xx.xx.xx.xx.
bindした状態 Proto LocalAdddress ForeignAddress State TCP *.A *.* Closed クライアント プロセス サーバ プロセス Port A Port B Port C ホストA ホストB IP Address: xx.xx.xx.xx. IP Address: xx.xx.xx.xx.
Listen() Proto LocalAdddress ForeignAddress State TCP *.A *.* Listen クライアント プロセス サーバ プロセス Port A Port B Port C ホストA ホストB IP Address: xx.xx.xx.xx. IP Address: xx.xx.xx.xx.
Listen()拡大 Proto LocalAdddress ForeignAddress State TCP *.A *.* Listen サーバ プロセス Port A Port B 3Way Hand Shakeを済ました接続要求やSYNを保持するためのキュー Port C ホストB IP Address: xx.xx.xx.xx.
クライアントがConnect() Proto LocalAdddress ForeignAddress State TCP *.A *.* SYN_RCVD Connect() SYN クライアント プロセス サーバ プロセス Port X Port A Port B Port C ホストA ホストB IP Address: xx.xx.xx.xx. IP Address: yy.yy.yy.yy
キューの拡大 Proto LocalAdddress ForeignAddress State TCP *.A *.* SYN_RCVD サーバ プロセス 埋まり Port A 空き Port B 3Way Hand Shakeを済ました接続要求やSYNをacceptされるまで保持するためのキュー Port C ホストB IP Address: xx.xx.xx.xx.
Accept() Proto LocalAdddress ForeignAddress State TCP *.A *.* Listen TCP xx.xx.xx.xx.A yy.yy.yy.yy.X Establish Connect() クライアント プロセス サーバ プロセス Port X Port A Port B Port C ホストA ホストB IP Address: xx.xx.xx.xx. IP Address: yy.yy.yy.yy