第6回NP資料 tcpを使った通信

Download Report

Transcript 第6回NP資料 tcpを使った通信

第6回ネットワークプ
ログラミング
中村 修
今日のお題

講義
 TCPとは?

練習1: tcpを使ったecho clientを書こう
 名前の解決

練習2 : gethostbynameに慣れよう
---------休憩------------------------------- 実習:TCPでデータを送る/受け取る
 TCPで
echo serverを作ろう
トランスポート層の役割

届いたデータをどのプログラムに渡す?


ポート番号 : /etc/services
アプリケーションに通信の質の選択を提供

複雑だが信頼性を保証する通信
TCP
 Connection Oriented
 Virtual Circuit


単純だが信頼性を保証しない通信
UDP
 Connection Less
 Datagram

TCP vs UDP
ネットワークを利用するアプリケーションは主に
トランスポートプロトコルとしてTCPとUDPを利用する。
TCP
•Reliable
•Sequence Control
•Flow Control
•Congestion Control
•Retransmission
•Bit-based charging
UDP
•Unreliable
•No Sequence Control
•No Flow Control
•No Congestion Control
•No Retransmission
•Fixed-rate charging
UDP
(User Datagram Protocol)



信頼性がない
データレート制御:可
身近な例
 DNS
イメージ
はがき
データ
データ
データ
 はがきのやりとり
S
client
データ
 ストリーミング

server
C
データ
TCP
(Transmission Control Protocol)



信頼性のある通信を提供
データレート制御:不可・困難
身近な例


電子メール
WEB
イメージ
電話のやりとり




S:もしもし
C:もしもし、○○です
S:××です。あのさ、、、
server
client
SYN
SYN+ACK
ACK
データ
TCPが提供する信頼性

IPが提供する通信に信頼性を与える
コネクション型通信
 タイムアウトと再転送
 エラー検出とエラー訂正
 フロー制御
 順序の再構成

 データ転送に関するインターフェイスを提供
※それでも必ずデータが届くわけではない
輻輳制御ルール1:
返事で判断
返事が早い→空いてる→沢山送る
輻輳制御ルール2:
混んだら小さく
返事が遅い→混んでる→少し送る
輻輳制御ルール3:
無くなったらしばらくたってもう一度
返事が無い→データが無くなった?→もう一度送る
輻輳制御ルール4:
空いても急ぐな
返事が早い→空いてる→ゆっくり送る
TCPヘッダ

ポート番号



シーケンス番号


あるデータを送る時、そのデータがどの要に連続しているかを
シーケンシャル(連続的)に示す番号(何番目のバイトか)
ack番号



送信者側(source)と受信者側(destination)
16bit (u_short)
送られたデータに対してackを返す場合に、どのシーケンス番号
の確認応答なのかを示す番号
確認応答したいシーケンス番号+1
フラグ

それぞれのパケットの状態を示す




その通信の最初のパケットなのか
届いたという確認なのか
もう終わりなのか
強制終了なのか
コネクション開始
送信側
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
接続が中断する状況

ネットワークの停止/ピアホストのクラッシュ




ピアアプリケーションのクラッシュ


送信側は約9分再試行の後タイムアウト
受信側はポーリングしない
Operation Timed Out
(ETIMEDOUT)/SIGPIPE/EPIPE/ENETURNREACH/EHOSTUNR
EACH
SIGPIPE/EPIPE
ピアホストのリブート


Connection Reset by Peer (ECONNRESET)
Broken Pipe (SIGPIPE/EPIPE)
⇒プログラマによる対処が必要
データの転送



Sequence NumberとAcknowledge Numberによって、デー
タの順番を保証
ACKが帰ってきたら次のデータを送る
ACKが帰ってこなかったら前のデータを再転送
SEQ=231 DATA
SEQ=456 ACK=232
SEQ=232 DATA
データ転送と効率化

いちいちACKを待つと効率が悪い
 パケット毎に確認する必要があるか?
 送信者

推測を元にある程度先送りした方が良い
 受信者


ある程度まとめてackを送る方が良い
だが、先送りしすぎるとACKの戻りが遅れる
 先送りする幅を決めなければならない
ウィンドウコントロール(受信側)

先送りする幅を決める仕組み
 受信側が決める

送るパケットを制限する
SEQ=238
ACK=239
WindowSize=3
SEQ=270
SEQ=302
SEQ=334
3
ウィンドウコントロール(送信側)
ウィンドウサイズ=5
ウィンドウ
1
2
3
4
5
6
7
8
1~5までのデータを送れる
ウィンドウ
1
2
3
4
5
6
7
8
1のackが返ってきたら、
ウィンドウを2までずらす。
6まで送れるようになる。
8
2、3のackが返ってきたら、
ウィンドウを4までずらす。
8まで送れるようになる。
ウィンドウ
1
2
3
4
5
6
7
スロー・スタート

最初はなるべく小さなウィンドウサイズ
 通信し開始後、ネットワークの混雑度を把握

送信するパケットの送信速度を受信者から返され
るACKの速度、頻度(間隔)に同期
 ACKからネットワークの状態を推測

送り手のTCPのウィンドウが増加
 輻輳ウィンドウ(cwnd)
 新しいコネクションが確立
 輻輳ウィンドウをセグメント1つに初期化
 ACKが受け取られるたびに、輻輳ウィンドウが1セグメ
ントずつ増加
スロー・スタート
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
TCP まとめ

Transmission Control Protocol
 コネクションの確立

3way-handshake/piggy-back
 順序の再構成・フローコントロールなど
 信頼性を提供するための仕組み

スライディングウィンドウ・スロースタート
TCP 通信の流れ
TCP Server
socket()
TCP client
bind()
socket()
listen()
connect()
write()
establish
accept()
data
read()
data
write()
end
read()
read()
close()
close()
練習1:tcpでechoクライアント

TCPでechoクライアントを書いて見ましょう。

答えは以下にあり
/home/kaizaki/osamuNP/6/6-1.c
練習1: ポイント
sock_fd = socket(AF_INET, SOCK_STREAM, 0);
sin.sin_family = AF_INET;
sin.sin_port = htons(SERV_PORT);
inet_aton(SERV_ADDR,&sin.sin_addr);
connect(sock_fd, (struct sockaddr *)&sin, sizeof(sin));
fgets(buf,sizeof(buf),stdin);
write(sock_fd, buf, sizeof(buf));
readlen = read(sock_fd, buf, sizeof(buf));
printf("%s\n",buf);
補足:データサイズについて

read(),write()の際の
データサイズと、TCP
のデータ転送の際の
データサイズは異なる
プロセス
プロセス
TCP
TCP
名前の解決:www.sfc.keio.ac.jpにはどう
やってたどりつく?


ユーザ
IPアドレスから通信相
手まではたどり着ける
www.sfc.keio.ac.jpの
IPアドレスって何?
名前解決の例
•コンピューターは数字を理解する
www.sfc.keio.ac.jp
が見たいんだけど…
•一方人間は長い数字よりも
名前のほうが記憶しやすい
名前解決の必要性
ユーザ
133.27.4.212のサーバに
アクセスしてください
ネームサーバ
WEBサーバ
133.27.4.212
名前とアドレス

通信先は通信前に特定しなければならない
 ホストは一意のIPアドレスで識別される
 ホストは一意の名前で識別される(ユーザーの便宜のた
め)

名前
 可変長
 人間が記憶しやすい
 ホストの物理的な位置と無関係

アドレス
 固定長
 計算機が処理しやすい
 ルーティングによる制限がある
名前空間と名前解決

名前空間
 使用可能な名前の定義
 平面

or 階層
名前解決のメカニズム
 名前(ホスト名)から対応する値(IPアドレス)を見
つけるメカニズム

ネームサーバ
 名前解決のひとつの実現方法
 インターネット上でもっとも使われている
簡単な例
user
www.sfc.keio.ac.jp
www.sfc.keio.ac.jp
ネーム
サーバ
Web ブラウザ
133.27.4.212
133.27.4.212
Webサーバ
名前から値への
対応情報を提供
ではプログラマはどうすればいい?

プログラムの中で
名前 -> IPアドレスの変換を行えばよい
struct hostent *
gethostbyname(const char *name);
練習2: gethostbynameに慣れよう
第一引数にホスト名前を入力すると、
echo clientとして機能する。


答えは以下にあり
/home/kaizaki/osamuNP/6/6-2.c
練習2:ポイント
#include <netdb.h>
int main (int argc, char *argv[] ) {
int sock_fd;
struct sockaddr_in sin;
char buf[BUF_SIZE];
int readlen;
struct hostent *hp;
hp = gethostbyname( argv[1] ); /* add */
sock_fd = socket(AF_INET, SOCK_STREAM, 0);
sin.sin_family = AF_INET;
sin.sin_port = htons(SERV_PORT);
sin.sin_addr = *(struct in_addr *)hp->h_addr;
}
実習
 TCPを用いたechoサーバを作ろう。
第一引数にホスト名を指定しよう
 第二引数にポート番号を指定しよう

→次のページからに必要となる初出関数の説明
TCP 通信の流れ(おさらい)
TCP Server
socket()
TCP client
bind()
socket()
listen()
connect()
write()
establish
accept()
data
read()
data
write()
end
read()
read()
close()
close()
Socketを開いた状態
Socketを開く
クライアント
プロセス
Port A
サーバ
プロセス
Port B
Port C
ホストA
IP Address: xx.xx.xx.xx.
ホストB
IP Address: xx.xx.xx.xx.
bind()システムコール


int bind(int s, const struct sockaddr *addr, int addrlen)
用意したsokcetのアドレスを実際にsocketと結びつける

IPアドレスとTCP/UDPのポート番号の組
 開いたソケットのステートはclosed


実際のコードでは…
 bind(listenfd, (struct sockaddr *) &servaddr,
sizeof(servaddr)
成功なら0、エラーなら-1の返り値
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()システムコール


int listen(int s, int backlog)
用意したsocketを待ち受け準備状態にする


ソケットの状態をCLOSEDからLISTENへ
backlogはキューの長さ(backlog分の要求を保持できる)

accept()されるまでbacklog分のキューに保持
 キューがあふれると、その要求は無視


実際のコードでは…
 listen(listenfd, LISTENQ)
成功なら0、エラーなら-1の返り値
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.
accept()システムコール


int accept(int s, strucet sockaddr *addr, int *addrlen)
キューで待っている接続要求を取り出して、そのクライアント
と通信するためのディスクリプタを作成して、返す



clientにはクライアントのsocketのアドレス、namelenには
clientのサイズ
実際のコードでは…


clientにはクライアントのsocketのアドレス、namelenにはclient
のサイズ
accept(listenfd, (struct sockaddr *) &cliaddr, &clilen)
成功なら0、エラーなら-1の返り値
accept()
Proto LocalAdddress
TCP
xx.xx.xx.xx.A
ForeignAddress
State
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()
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.
使い方例
int socket_fd, accept_fd;
int client_addrlen;
int readlen;
struct sockaddr_in server, client;
socket_fd = socket(AF_INET,SOCK_STREAM, 0);
bind(socket_fd, (struct sockaddr *)&server, sizeof(struct sockaddr_in));
listen(socket_fd, 5);
memset((void *)&client, 0, sizeof(client));
client_addrlen = sizeof(client)
accept_fd = accept(socket_fd,(struct sockaddr *)&client, (int *)&client_addrlen);
---(read/writeなどの処理)---close(accept_fd);
close(socket_fd);
}
実習:それでは作ってみましょう!
 TCPを用いたechoサーバを作ろう。

第一引数にポート名を指定しよう
 TCPを用いたechoクライアントを作ろう。
第一引数にホスト名を指定しよう
 第二引数にポート名を指定しよう
