情報処理IIb(n) vol.1

Download Report

Transcript 情報処理IIb(n) vol.1

情報処理IIb(n) vol.4
慶應義塾大学
村井純
今日やること

コンピュータコミュニケーションの基礎
– トランスポート層
• TCP
– 輻輳制御
– 状態遷移

コンピュータコミュニケーションのモデル
– プロキシサーバ
TCP
(Transmission Control Protocol)


異なるホストのアプリケーション間に信頼性の
ある通信を提供
IPが提供する通信に信頼性を与える
•
•
•
•
•
コネクション型通信
タイムアウトと再転送
エラー検出とエラー訂正
フロー制御
順序の再構成
– データ転送に関するインターフェイスを提供
輻輳制御ルール1:返事で判断
返事が早い→空いてる→沢山送る
輻輳制御ルール2:混んだら小さく
返事が遅い→混んでる→少し送る
輻輳制御ルール2:混んだら小さく
返事が無い→混んでる→少し送る
輻輳制御ルール3:空いても急ぐな
返事が早い→空いてる→ゆっくり送る
バイト数/日
サンフランシスコ→藤沢(バイト数/日)
TCP/UDP利用別グラフ
14000000000
12000000000
10000000000
8000000000
6000000000
4000000000
2000000000
0
UDP
TCP
1
2
3
4 5
月
6
7
インターネットが遅い原因

返事が遅い
– 相手がのろい
– 途中がのろい

相手がのろい場合
– 相手が過負荷
• アクセスが多すぎる
• 力がなさ過ぎる

途中がのろい場合
– どこかの待ち行列であふれている
TCPヘッダ
Src Port
Dst Port
Sequence Number
Acknowledgement Number
Offset
Reserved
Flag
TCP Checksum
Option (if any)
Data
Window Size
Urgent Pointer
Padding
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
SYN_RCVD
Active Open
LISTEN
Send SYN
Recv RST
Send SYN
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=813
ACK=124
SEQ=123
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 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 Control(輻輳制御)

輻輳(Congestion)
– 中間ノードがデータグラムの過負荷によって
遅延が生じた状態.

輻輳を解決するために
– ウィンドウコントロール
– スロースタート
ウィンドウコントロール


4
先送りする幅を決める仕組み
送るパケットを制限する
SEQ=238
SEQ=270
SEQ=302
SEQ=334
ACK=335
4
スロー・スタート





ネットワークに送り出されるパケットの通信速度を
他方のエンドから返される確認応答の通信速度
を同期させるためのもの
送り手のTCPに別のウィンドウを追加する。
– 輻輳ウィンドウ(cwnd)
新しいコネクションが確立
輻輳ウィンドウをセグメント1つに初期化
ACKが受け取られるたびに、輻輳ウィンドウは1セ
グメントずつ増やされる
スロー・スタート animation
cwnd=1
1:513(512)ack1, win4096
1
ack513, win8192
cwnd=2
3
4
cwnd=3
6
7
cwnd=4
cwnd=5
2
513:1025(512)ack1, win4096
1025:1537(512)ack1, win4096
ack1025, win8192
1537:2049(512)ack1, win4096
5
2049:2561(512)ack1, win4096
ack1537, win8192
8
ack2049, win8192
9
まとめ




IPはホストホスト間の通信まで
トランスポート層はポート番号によってアプ
リケーション間の通信を実現
UDPはIPの提供する品質の通信
TCPはいろいろな機能を用いることで、信
頼性のある通信をアプリケーション間に提
供する
代返

代わりに返事をすること
– 授業



当事者がその場に入れないときに使う
クライアントがサーバに到達する代わり代
理に問い合わせる
Proxy Server
Proxy Server

本物のサービスの代わりに、その代理の
サービスを提供するサーバ
– WWW Proxy Server
Proxy
Client
本物server
Socket Programming2
慶応大学政策・メディア研究科
今泉英明
[email protected]
先週の課題について

20人が提出(49%)
– 締め切りを過ぎても再提出可能(出さないよりは大幅
にまし)

寄せられた問題点
–
–
–
–
–
バッファサイズが1024bytesじゃ少ない。
void main()はint main()にした方がいい。
サーバのソケットから読み出すべき。
他人の名前でメールできてしまう。
Subjectが書けるようになっていない
課題の問題点

サーバとうまくおしゃべりできていない
–
–
–
–
Read()がブロックしてしまう
サーバからしゃべりだしたりしたとき
サーバから1024bytes以上送られてきたとき
以下の順序に依存しているのが原因
1.
2.
3.
4.
標準入力から読む(read()でブロック)
ソケットに書き込む
ソケットから読む(read()でブロック)
標準出力に書き込む
今日の目標

ソケットプログラミングの基礎を学ぶ
– 非同期多重入出力
• 複数のディスクリプタを同時に利用しよう!
– TCPを用いるサーバを書こう
• UNIXプログラミングの勉強
– プロセスについて
非同期多重入出力



読み込み準備の整ったファイルディスクリ
プタから読みたい
書き込み準備の整ったファイルディスクリ
プタから読みたい
とにかく複数のディスクリプタから非同期
に読み書きがしたい
Select()システムコール

複数のディスクリプタを非同期に扱うため
のシステムコール
– ファイルディスクリプタの集合を渡して、読み
出し/書き込み可能なファイルディスクリプタを
教えてもらう
– 一定のタイムアウトも指定できる
fd_set


ファイルディスクリプタの集合を保持するための
ビットマップ
以下のような10bitのビットマップにファイルディ
スクリプタ0, 4, 8という集合を表現させると
0 1 2 3 4 5 67 8 9
0 1 2 3 4 5 67 8 9
0 0 0 0 0 0 0 0 0 0
1 0 0 0 1 0 0 0 1 0
10bitsあると10個のファイルディスクリプタの集合を
表現することができる
•FreeBSD3.2, Solaris 2.6では1024bits
•BSD/OS3.1では256bits
fd_setの操作

以下のインターフェースを利用して操作
fd_set fds;
FD_ZERO(&fds);
FD_SET(fd, &fds);
FD_ISSET(fd, &fds);
FD_CLR(fd, &fds);
/*fdsをゼロに初期化*/
/*fdsにfd番目のビットを上げる*/
/*fdsにfd番目のビットが設定さ
れているか調べる*/
/*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はタイムアウトするまでのタイマ値(秒、マイクロ秒)
timeval構造体


longである秒とマイクロ秒の二つのメンバ
タイマの値を指定できる
long tv_sec
long tv_usec
タイマの設定

Selectは以下の3つのタイマの指定が可能
1. NULLを指定
- タイムアウトしない
2. Timeval構造体を指定
- 指定された一定時間でタイムアウト
3. 0秒0マイクロ秒に指定したTimeval構造体を指定
- ファイルディスクリプタを検査した後すぐにタイムアウト
Selectの手順1
ファイルディスクリプタ0, 4, 8という集合のうちどれ
が読み出し可能になったか知りたい場合
int nfds;
0 1 2 3 4
fd_set fds;
0 0 0 0 0
FD_ZERO(&fds);
1 0 0 0 0
FD_SET(0, &fds);
1 0 0 0 1
FD_SET(4, &fds);
FD_SET(8, &fds);
1 0 0 0 1
nfds = 8 + 1;
select(nfds, &fds, NULL, NULL, NULL);
 読み出し可能なビットが立つ
1 0 0 0 0
5
0
0
0
0
67
0 0
0 0
0 0
0 0
8
0
0
0
1
9
0
0
0
0
0 0 0 1 0
Selectの手順2
Selectを実行した後の処理
select(nfds, &fds, NULL, NULL, NULL);
 読み出し可能なビットが立つ
if(FD_ISSET(0, &fds){
0 1 2 3 4 5 67 8 9
read(0, ….);
1 0 0 0 0 0 0 0 1 0
}
if(FD_ISSET(4, &fds){
read(4, ….);
}
if(FD_ISSET(8, &fds){
read(8, …..);
}
実際に変更

別紙参照
ここまでのまとめ



ネットワークプログラミングをするにはソケットを
利用する
ネットワークに流す複数バイトのデータ型
(long,short)はネットワークバイトオーダにあわ
せる
TCPでコネクションを張るとbyte streamが流れ
るパイプが張れる
– その上に流れるデータはそれを扱うプログラムが担う

複数のディスクリプタを非同期に扱うには
select()システムコールを利用する。
サーバ側を書いてみよう


サーバも同様にTCPのコネクションが張れると単
なるディスクリプタ
違いは、みんなからの接続要求を待つためのソ
ケットを開く
– 自分でソケットにアドレスをbindしなければならない
– 接続要求を保持するためにListen()を呼んでキュー
を作成
– 要求にaccept()して、それぞれのクライアントと通信
するためのディスクリプタを作成

あとは一緒
システムコールの流れ
TCPクライアント
TCPサーバ
socket()
socket()
connect()
bind()
listen()
write()
accept()
read()
read()
write()
close()
EOFの通知
read()
close()
プログラムの流れ
1.
2.
3.
4.
5.
6.
7.
Sockaddr_in構造体に、サーバのIPアドレス
とポート番号を設定
ソケットを開く
1のアドレスをソケットにbind()
Listen(接続待機)でキューを作成
接続要求に対してAccept()
おしゃべり
Close()
初期状態
クライアント
プロセス
Port A
サーバ
プロセス
Port B
Port C
ホストA
IP Address: xx.xx.xx.xx.
ホストB
IP Address: xx.xx.xx.xx.
Socketを開いた状態
Socketを開く
クライアント
プロセス
Port A
サーバ
プロセス
Port B
Port C
ホストA
IP Address: xx.xx.xx.xx.
ホストB
IP Address: xx.xx.xx.xx.
bindした状態
Proto LocalAdddress
TCP
*.A
クライアント
プロセス
ForeignAddress
*.*
Port A
State
Closed
サーバ
プロセス
Port B
Port C
ホストA
IP Address: xx.xx.xx.xx.
ホストB
IP Address: xx.xx.xx.xx.
Listen()
Proto LocalAdddress
TCP
*.A
クライアント
プロセス
ForeignAddress
*.*
Port A
State
Listen
サーバ
プロセス
Port B
Port C
ホストA
IP Address: xx.xx.xx.xx.
ホストB
IP Address: xx.xx.xx.xx.
Listen()拡大
Proto LocalAdddress
TCP
*.A
Port A
Port B
Port C
ForeignAddress
*.*
サーバ
プロセス
3Way Hand Shakeを済ました接続
要求やSYNを保持するための
キュー
ホストB
IP Address: xx.xx.xx.xx.
State
Listen
クライアントがConnect()
Proto LocalAdddress
TCP
*.A
ForeignAddress
State
*.*
SYN_RCVD
Connect()
SYN
クライアント
プロセス
Port X
Port A
サーバ
プロセス
Port B
Port C
ホストA
IP Address: yy.yy.yy.yy
ホストB
IP Address: xx.xx.xx.xx.
キューの拡大
Proto LocalAdddress
TCP
*.A
Port A
Port B
Port C
ForeignAddress
State
*.*
SYN_RCVD
サーバ
プロセス
3Way Hand Shakeを済ました接続
要求やSYNをacceptされるまで保
持するためのキュー
ホストB
IP Address: xx.xx.xx.xx.
埋まり
空き
Accept()
Proto LocalAdddress
TCP
*.A
TCP
xx.xx.xx.xx.A
ForeignAddress
State
*.*
Listen
yy.yy.yy.yy.X
Establish
Connect()
クライアント
プロセス
Port X
Port A
サーバ
プロセス
Port B
Port C
ホストA
IP Address: yy.yy.yy.yy
ホストB
IP Address: xx.xx.xx.xx.
新しいシステムコール



bind()
listen()
accept()
bind()

int bind(int s, struct sockaddr *name, int
namelen);
– ソケットに名前(アドレス)を付ける
– アドレスにINADDR_ANYを指定した場合は、自分の
持つすべてのアドレスに対しての要求を受け付ける
• *.port-number
• そのサービスを提供するアドレスを制限できる
– 開いたソケットのステートはclosed
同じポート番号を利用する
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
TCP xx.xx.xx.xx.10001
TCP yy.yy.yy.yy.10001
TCP
*.12345
ForeignAddress
*.*
*.*
*.*
State
closed
closed
closed
port 10001へのクライアントの要求は、
目的アドレスによって、異なるサーバが答えることになる
絵にすると、、、
サーバA
Port A
Port A
Port B
サーバB
Port C
Port C
Address: xx.xx.xx.xx.
Port B
サーバC
Address: zz.zz.zz.zz
ホストB
アドレス毎に独立したポート表を持つ
listen()

int listen(int s, int backlog);
– s は ソケットディスクリプタ
– backlogはキューの長さ(backlog分の要求
を保持できる)
• 接続要求は、accept()されるまでbacklog長の
キューに保持される
• キューがあふれると、その要求は無視
キューの拡大
Proto LocalAdddress
TCP
*.A
ForeignAddress
State
*.*
SYN_RCVD
サーバ
プロセス
Port A
Port B
backlog=4のキュー
Port C
ホストB
IP Address: xx.xx.xx.xx.
埋まり
空き
accept()

int accept(int s, struct sockaddr
*client, int *namelen)
– キューで待っている接続要求を取り出して、そのクラ
イアントと通信するためのディスクリプタを作成して、
返す
– clientにはクライアントのsocketのアドレス、
namelenにはclientのサイズ
Accept()
Proto LocalAdddress
TCP
*.A
TCP
xx.xx.xx.xx.A
ForeignAddress
State
*.*
Listen
yy.yy.yy.yy.X
Establish
Connect()
クライアント
プロセス
Port X
Port A
サーバ
プロセス
Port B
Port C
ホストA
IP Address: yy.yy.yy.yy
ホストB
IP Address: xx.xx.xx.xx.
accept()の後


accept()で返ってきたファイルディスクリプ
タを利用してクライアントとしゃべる
要求待機用のソケットもselectで扱える
– listenしているsocketが読み込み可能なとき
はaccept可能なとき

selectをもちいることで、多くのクライアント
の要求に答えることができる
サーバの気持ちになってみよう



そんなたくさんのソケットを扱ったり、めん
どくさいことはしたくない
沢山のクライアントが接続してきたら、もう
大変
猫の手も借りたいわ!
えーい、クローンだ。

forkシステムコール
– int fork();
– 自分とまったく同じ複製プロセスを作るシステ
ムコール
– 0が帰ってきたら自分は子プロセス
– 正の整数が帰ってきたら自分はfork()を呼び
出した親プロセス
– -1は失敗
処理の流れ

接続要求があったら、acceptして,とりあえ
ずfork();
– 子プロセスだったら、サービスの提供開始
– 親プロセスだったら、接続要求待ち
シグナル

は来週
まとめ

TCPサーバのプログラムは、
–
–
–
–
Socketを開く
socketにアドレス(Ipaddress, port 番号)をbind
listen()でキューを作成
accept()でそれぞれのクライアントへのディスクリプタ
を作成
– おしゃべり
– 最後にclose()
今週の課題

fork()を利用せずに、select()のみを利用
して、echoサーバを作りなさい。
– listenのディスクリプタもaccept()でもらった
ディスクリプタもselectで非同期多重
– Cの勉強なので、がんばってね。
– 完全でなくても出してください。
締め切り


10月25日午後11時59分59秒
いつもどおりSOIのシステムで提出