情報処理 iib(n) vol.4
DESCRIPTION
情報処理 IIb(n) vol.4. 慶應義塾大学 村井純. 今日やること. コンピュータコミュニケーションの基礎 トランスポート層 TCP 輻輳制御 状態遷移 コンピュータコミュニケーションのモデル プロキシサーバ. TCP (Transmission Control Protocol). 異なるホストのアプリケーション間に信頼性のある通信を提供 IP が提供する通信に信頼性を与える コネクション型通信 タイムアウトと再転送 エラー検出とエラー訂正 フロー制御 順序の再構成 - PowerPoint PPT PresentationTRANSCRIPT
情報処理情報処理 IIb(n) vol.4IIb(n) vol.4
慶應義塾大学村井純
今日やること今日やること
コンピュータコミュニケーションの基礎–トランスポート層• TCP
– 輻輳制御– 状態遷移
コンピュータコミュニケーションのモデル–プロキシサーバ
TCP TCP (Transmission Control Protocol)(Transmission Control Protocol)
異なるホストのアプリケーション間に信頼性のある通信を提供
IP が提供する通信に信頼性を与える•コネクション型通信•タイムアウトと再転送•エラー検出とエラー訂正•フロー制御•順序の再構成
– データ転送に関するインターフェイスを提供
輻輳制御ルール1:返事で判断輻輳制御ルール1:返事で判断
返事が早い→空いてる→沢山送る返事が早い→空いてる→沢山送る
輻輳制御ルール輻輳制御ルール 22 :混んだら小さく:混んだら小さく
返事が遅い→混んでる→少し送る返事が遅い→混んでる→少し送る
輻輳制御ルール輻輳制御ルール 22 :混んだら小さく:混んだら小さく
返事が無い→混んでる→少し送る返事が無い→混んでる→少し送る
輻輳制御ルール輻輳制御ルール 33 :空いても急ぐな:空いても急ぐな
返事が早い→空いてる→ゆっくり送る返事が早い→空いてる→ゆっくり送る
サンフランシスコ→藤沢(バイト数サンフランシスコ→藤沢(バイト数 //日)日) TCP/UDPTCP/UDP 利用別グラフ利用別グラフ
02000000000400000000060000000008000000000100000000001200000000014000000000
1 2 3 4 5 6 7
月
/バ
イト
数日
UDPTCP
インターネットが遅い原因インターネットが遅い原因 返事が遅い–相手がのろい–途中がのろい
相手がのろい場合–相手が過負荷• アクセスが多すぎる• 力がなさ過ぎる
途中がのろい場合–どこかの待ち行列であふれている
TCPTCP ヘッダヘッダ
Src PortSrc Port Dst PortDst Port
Sequence NumberSequence Number
Acknowledgement NumberAcknowledgement Number
OffsetOffset ReservedReserved FlagFlag Window SizeWindow Size
TCP ChecksumTCP Checksum Urgent PointerUrgent Pointer
Option (if any)Option (if any) PaddingPadding
DataData
Flags CODEFlags CODE
Flags は TCP がコネクション制御に利用 6 つの flags がある
– URG : Urgent Pointer が有効– ACK : Acknowledge Number が有効– PSH : セグメントをすぐに上位層へ渡す– RST : エラーによる強制的なクローズ– SYN : コネクションセットアップの同期をとる– FIN : コネクションを終了する
TCPTCP の状態遷移図の状態遷移図
SYN_SENT
ESTABLISHED
CLOSED
LISTEN
SYN_RCVD
Passive OpenActive Open
Send SYNRecv SYN
send SYN,ACK Send SYN
Recv SYN
Recv ACK Recv SYN,ACKSend ACK
Recv RST
データ転送状態
コネクション開始コネクション開始
送信側送信側 受信側受信側
SEQ=123SEQ=123
SEQ=123SEQ=123
ACK=813ACK=813
SEQ=812SEQ=812
SEQ=813SEQ=813ACK=124ACK=124
SYN SEQ=812SYN SEQ=812 No ACKNo ACK
SYN SEQ=123 ACK=813SYN SEQ=123 ACK=813
SEQ=813 ACK=124SEQ=813 ACK=124
3way Handshake3way Handshake コネクションのセットアップ
– ホスト 1 、ホスト 2 間で•ホスト 1 は SYN Flag のついたパケットを送る•SYN を受けたホスト 2 は相手との同期を取るために
SYN Flag のついたパケットを送る。この時いっしょに受け取った SYN に対する ACK Flag もつける
•ホスト 2 から SYN を受け取ったホスト 1 は ACK を返す
– SYN と ACK が相乗り•Piggy Back
コネクションの終了コネクションの終了 コネクションの終了は FIN Flag によって行う コネクションの終了は一方ごとにできる
FIN SEQ=457
SEQ=789 ACK=458
FIN SEQ=790
SEQ=458 ACK=791
データの転送データの転送 Sequence Number と Acknowledge Number によって、
データの順番を保証 ACK が帰ってきたら次のデータを送る ACK が帰ってこなかったら前のデータを再転送
SEQ=231 DATA
SEQ=456 ACK=232
SEQ=232 DATA
データ転送(続き)データ転送(続き)
いちいち ACK を待つと効率が悪い 推測を元にある程度先送りした方が良い だが、先送りしすぎると ACK がなかなか
帰ってこない 幅を決めなければならない
Congestion ControlCongestion Control(( 輻輳制御輻輳制御 ))
輻輳 (Congestion)–中間ノードがデータグラムの過負荷によっ
て遅延が生じた状態. 輻輳を解決するために–ウィンドウコントロール–スロースタート
ウィンドウコントロールウィンドウコントロール
先送りする幅を決める仕組み 送るパケットを制限する
44
ACK=335ACK=335
44
SEQ=238SEQ=238SEQ=270SEQ=270SEQ=302SEQ=302SEQ=334SEQ=334
スロー スタート・スロー スタート・ ネットワークに送り出されるパケットの通信速度を他方のエンドから返される確認応答の通信速度を同期させるためのもの
送り手の TCP に別のウィンドウを追加する。– 輻輳ウィンドウ (cwnd)
新しいコネクションが確立 輻輳ウィンドウをセグメント 1 つに初期化 ACK が受け取られるたびに、輻輳ウィンドウ
は 1 セグメントずつ増やされる
スロー・スタート スロー・スタート animationanimation
cwnd=1 12
cwnd=234
5cwnd=3
6
7
8
1:513(512)ack1, win4096
ack513, win8192
513:1025(512)ack1, win4096
1025:1537(512)ack1, win4096
ack1025, win8192
ack1537, win8192
1537:2049(512)ack1, win4096
2049:2561(512)ack1, win4096
cwnd=4ack2049, win8192 9cwnd=5
まとめまとめ
IP はホストホスト間の通信まで トランスポート層はポート番号によっ
てアプリケーション間の通信を実現 UDP は IP の提供する品質の通信 TCP はいろいろな機能を用いることで、
信頼性のある通信をアプリケーション間に提供する
代返代返
代わりに返事をすること– 授業
当事者がその場に入れないときに使う
クライアントがサーバに到達する代わり代理に問い合わせる
Proxy Server
Proxy ServerProxy Server 本物のサービスの代わりに、その代理
のサービスを提供するサーバ– WWW Proxy Server
Proxy
本物 server
Client
先週の課題について先週の課題について
20人が提出 (49%)– 締め切りを過ぎても再提出可能(出さないよりは
大幅にまし ) 寄せられた問題点– バッファサイズが 1024bytesじゃ少ない。– void main() は int main() にした方がいい。– サーバのソケットから読み出すべき。– 他人の名前でメールできてしまう。– Subject が書けるようになっていない
課題の問題点課題の問題点
サーバとうまくおしゃべりできていない– Read() がブロックしてしまう– サーバからしゃべりだしたりしたとき– サーバから 1024bytes以上送られてきたとき– 以下の順序に依存しているのが原因1. 標準入力から読む (read() でブロック )
2. ソケットに書き込む3. ソケットから読む (read() でブロック )
4. 標準出力に書き込む
今日の目標今日の目標
ソケットプログラミングの基礎を学ぶ–非同期多重入出力• 複数のディスクリプタを同時に利用しよう!
– TCP を用いるサーバを書こう• UNIX プログラミングの勉強
– プロセスについて
非同期多重入出力非同期多重入出力
読み込み準備の整ったファイルディスクリプタから読みたい
書き込み準備の整ったファイルディスクリプタから読みたい
とにかく複数のディスクリプタから非同期に読み書きがしたい
Select()Select() システムコールシステムコール
複数のディスクリプタを非同期に扱うためのシステムコール–ファイルディスクリプタの集合を渡して、読み出し /書き込み可能なファイルディスクリプタを教えてもらう
–一定のタイムアウトも指定できる
fd_setfd_set ファイルディスクリプタの集合を保持するためのビットマップ 以下のような 10bit のビットマップにファイルディスクリプタ
0, 4, 8 という集合を表現させると
0 0 0 0 0 00 0 0 0
0 1 2 3 4 5 6 7 8 9
1 0 0 0 1 00 0 0 1
0 1 2 3 4 5 6 7 8 9
•FreeBSD3.2, Solaris 2.6 では 1024bits•BSD/OS3.1 では 256bits
10bits あると 10個のファイルディスクリプタの集合を表現することができる
fd_setfd_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() 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 はタイムアウトするまでのタイマ値(秒、マイクロ秒)
timevaltimeval 構造体構造体
long である秒とマイクロ秒の二つのメンバ タイマの値を指定できる
long tv_sec
long tv_usec
タイマの設定タイマの設定
Select は以下の3つのタイマの指定が可能1. NULL を指定
- タイムアウトしない
2. Timeval 構造体を指定- 指定された一定時間でタイムアウト
3. 0秒 0マイクロ秒に指定した Timeval 構造体を指定
- ファイルディスクリプタを検査した後すぐにタイムアウト
SelectSelect の手順の手順 11ファイルディスクリプタ 0, 4, 8 という集合のうち
どれが読み出し可能になったか知りたい場合
0 0 0 0 0 00 0 0 0
0 1 2 3 4 5 6 7 8 9int 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); 読み出し可能なビットが立つ
1 0 0 0 0 00 0 0 01 0 0 0 1 00 0 0 01 0 0 0 1 00 0 0 1
1 0 0 0 0 00 0 0 1
SelectSelect の手順の手順 22Select を実行した後の処理
0 1 2 3 4 5 6 7 8 9
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, …..);}
1 0 0 0 0 00 0 0 1
実際に変更実際に変更
別紙参照
ここまでのまとめここまでのまとめ ネットワークプログラミングをするにはソケッ
トを利用する ネットワークに流す複数バイトのデータ型( lon
g,short) はネットワークバイトオーダにあわせる
TCP でコネクションを張ると byte stream が流れるパイプが張れる– その上に流れるデータはそれを扱うプログラムが担
う 複数のディスクリプタを非同期に扱うには selec
t() システムコールを利用する。
サーバ側を書いてみようサーバ側を書いてみよう サーバも同様に TCP のコネクションが張れる
と単なるディスクリプタ 違いは、みんなからの接続要求を待つためのソケットを開く– 自分でソケットにアドレスを bind しなければなら
ない– 接続要求を保持するために Listen() を呼んでキュー
を作成– 要求に accept() して、それぞれのクライアントと
通信するためのディスクリプタを作成 あとは一緒
システムコールの流れシステムコールの流れ
socket()
connect()
write()
read()
close()
TCP クライアント TCP サーバ
read()
write()
close()
read()EOF の通知
socket()
bind()listen()
accept()
プログラムの流れプログラムの流れ
1. Sockaddr_in 構造体に、サーバの IP アドレスとポート番号を設定
2. ソケットを開く3. 1のアドレスをソケットに bind()
4. Listen(接続待機 ) でキューを作成5. 接続要求に対して Accept()
6. おしゃべり7. Close()
初期状態初期状態
クライアントプロセス
ホスト A
サーバプロセス
ホスト B
Port B
IP Address: xx.xx.xx.xx.IP Address: xx.xx.xx.xx.
Port C
Port A
SocketSocket を開いた状態を開いた状態
クライアントプロセス
ホスト A
サーバプロセス
ホスト B
Port B
IP Address: xx.xx.xx.xx.IP Address: xx.xx.xx.xx.
Port C
Port A
Socket を開く
bindbind した状態した状態
クライアントプロセス
ホスト A
サーバプロセス
ホスト B
Port B
IP Address: xx.xx.xx.xx.IP Address: xx.xx.xx.xx.
Port C
Port A
Proto LocalAdddress ForeignAddress StateTCP *.A *.* Closed
Listen()Listen()
クライアントプロセス
ホスト A
サーバプロセス
ホスト B
Port B
IP Address: xx.xx.xx.xx.IP Address: xx.xx.xx.xx.
Port C
Port A
Proto LocalAdddress ForeignAddress StateTCP *.A *.* Listen
Listen()Listen()拡大拡大
サーバプロセス
ホスト B
Port B
IP Address: xx.xx.xx.xx.
Port C
Port A
Proto LocalAdddress ForeignAddress StateTCP *.A *.* Listen
3Way Hand Shake を済ました接続要求や SYN を保持するためのキュー
クライアントがクライアントが Connect()Connect()
クライアントプロセス
ホスト A
サーバプロセス
ホスト B
Port B
IP Address: xx.xx.xx.xx.IP Address: yy.yy.yy.yy
Port C
Port APort X
Connect()
Proto LocalAdddress ForeignAddress StateTCP *.A *.* SYN_RCVD
SYN
キューの拡大キューの拡大
サーバプロセス
ホスト B
Port B
IP Address: xx.xx.xx.xx.
Port C
Port A
Proto LocalAdddress ForeignAddress StateTCP *.A *.* SYN_RCVD
3Way Hand Shake を済ました接続要求や SYN を accept されるまで保持するためのキュー
埋まり空き
Accept()Accept()
クライアントプロセス
ホスト A
サーバプロセス
ホスト B
Port B
IP Address: xx.xx.xx.xx.IP Address: yy.yy.yy.yy
Port C
Port APort X
Connect()
Proto LocalAdddress ForeignAddress StateTCP *.A *.* ListenTCP xx.xx.xx.xx.A yy.yy.yy.yy.X Establish
新しいシステムコール新しいシステムコール
bind() listen() accept()
bind() bind()
int bind(int s, struct sockaddr *name, int namelen);– ソケットに名前 ( アドレス ) を付ける– アドレスに INADDR_ANY を指定した場合は、自分の持つすべてのアドレスに対しての要求を受け付ける• *.port-number• そのサービスを提供するアドレスを制限できる
– 開いたソケットのステートは closed
同じポート番号を利用する同じポート番号を利用する2つのサーバ2つのサーバ
あるホスト A は xx.xx.xx.xx と yy.yy.yy.yy の二つのアドレスを持つ– サーバ A のソケットを xx.xx.xx.xx, port 10001 に bind– サーバ B のソケットを yy.yy.yy.yy, port 10001 に bind– サーバ C のソケットを INADDR_ANY, port 12345 に bind
socket の状態はProto LocalAdddress ForeignAddress StateTCP xx.xx.xx.xx.10001 *.* closedTCP yy.yy.yy.yy.10001 *.* closedTCP *.12345 *.* closed
port 10001 へのクライアントの要求は、目的アドレスによって、異なるサーバが答えることになる
絵にすると、、、絵にすると、、、
サーバ A
ホスト B
Port B
Address: xx.xx.xx.xx.
Port C
Port A
アドレス毎に独立したポート表を持つ
サーバ B Port B
Port C
Port A
Address: zz.zz.zz.zzサーバ C
listen()listen()
int listen(int s, int backlog);– s は ソケットディスクリプタ– backlog はキューの長さ (backlog分の要求を保持できる )• 接続要求は、 accept ()されるまで backlog長のキューに保持される• キューがあふれると、その要求は無視
キューの拡大キューの拡大
サーバプロセス
ホスト B
Port B
IP Address: xx.xx.xx.xx.
Port C
Port A
Proto LocalAdddress ForeignAddress StateTCP *.A *.* SYN_RCVD
backlog=4 のキュー
埋まり空き
accept()accept()
int accept(int s, struct sockaddr *client, int *namelen)– キューで待っている接続要求を取り出して、その
クライアントと通信するためのディスクリプタを作成して、返す
– client にはクライアントの socket のアドレス、namelen には client のサイズ
Accept()Accept()
クライアントプロセス
ホスト A
サーバプロセス
ホスト B
Port B
IP Address: xx.xx.xx.xx.IP Address: yy.yy.yy.yy
Port C
Port APort X
Connect()
Proto LocalAdddress ForeignAddress StateTCP *.A *.* ListenTCP xx.xx.xx.xx.A yy.yy.yy.yy.X Establish
accept()accept() の後の後 accept() で返ってきたファイルディスク
リプタを利用してクライアントとしゃべる
要求待機用のソケットも select で扱える– listen している socket が読み込み可能なと
きは accept可能なとき select をもちいることで、多くのクライ
アントの要求に答えることができる
サーバの気持ちになってみよサーバの気持ちになってみようう
そんなたくさんのソケットを扱ったり、めんどくさいことはしたくない
沢山のクライアントが接続してきたら、もう大変
猫の手も借りたいわ!
えーい、クローンだ。えーい、クローンだ。
fork システムコール– int fork();–自分とまったく同じ複製プロセスを作るシ
ステムコール– 0 が帰ってきたら自分は子プロセス–正の整数が帰ってきたら自分は fork() を呼び出した親プロセス
– -1 は失敗
処理の流れ処理の流れ
接続要求があったら、 accept して , とりあえず fork();–子プロセスだったら、サービスの提供開始–親プロセスだったら、接続要求待ち
シグナルシグナル
は来週
まとめまとめ
TCP サーバのプログラムは、– Socket を開く– socket にアドレス (Ipaddress, port 番号 ) を bin
d– listen() でキューを作成– accept() でそれぞれのクライアントへのディス
クリプタを作成– おしゃべり– 最後に close()
今週の課題今週の課題 fork() を利用せずに、 select() のみを
利用して、 echo サーバを作りなさい。– listen のディスクリプタも accept() でも
らったディスクリプタも select で非同期多重
– C の勉強なので、がんばってね。–完全でなくても出してください。
締め切り締め切り
10 月 25 日午後11時59分59秒 いつもどおり SOI のシステムで提出