データってどうやって送信されているの?
私は前の記事で、HTTPに則ってWebブラウザとWebサーバの通信に適したアプリケーションデータ(リクエスト・メッセージ)が作成されることを理解しました。
しかし、実際にそのデータが送信される時の仕組みについては理解できていません。今回は相手にデータを届けるためのプロトコルである、TCP
についてまとめていきます。
これは何の記事か?
プログラミングしか学んでこなかったエンジニアが本を参考に、TCP
の仕組みについて大まかにまとめた記事です。
対象読者
- プログラミングしか学んでこなかった
-
TCP
ってなんですか??
この記事のゴール
-
TCP
でのデータ通信の特徴について、大まかに説明ができる。
全体像の把握
この記事は、WebブラウザがWebサーバに対してデータを送信するときの、トランスポート層で行われる処理のうち、TCP
についてまとめたものになります。(赤枠の部分)
トランスポート層の役割は?
トランスポート層の役割は、「データを通信相手に届けること」であり、通信相手のアプリケーション層にあるどのプロトコルに渡すかまで責任を持ちます。
TCPとUDPの違い
トランスポート層は上記の役割を担いますが、それを実現しているプロトコルがTCP
とUDP
です。取り扱うデータの性質によって使用するプロトコルが異なります。
プロトコル | 取り扱うデータの特徴 | 例 |
---|---|---|
TCP | 信頼性が必要とされるデータ | Web、メール、ファイル共有など |
UDP | 即時性が必要とされるデータ | DNS、音声、映像など |
まとめると、TCP
は通信相手にデータを確実に届ける(ような仕組み)けど、通信に時間がかかるプロトコルであり、UDP
はデータを送ったら送りっぱなしで信頼性は低いけど、通信に時間がかからないプロトコルという特徴を持っています。
なぜ通信にかかる時間に違いがあるのか?
なぜTCP
とUDP
とでは、通信にかかる時間に違いがあるのでしょうか。これはTCP
とUDP
でのやりとりの様子をそれぞれ知ることで理解することができます。
今回はまずTCP
に則った通信が実際にどのように行われるかを紹介していきます。
TCPでの通信の流れ
1. 準備(ソケットの作成)
Webブラウザ
アプリケーション(ここではWebブラウザ)がSocket
ライブラリのsocket
というプログラムを呼び出して、プロトコル・スタックにソケットを作成するよう依頼します。
クライアント側のプロトコル・スタック
ソケットひとつ分のメモリー領域を確保することで、そこにソケットの制御情報を記録していく準備を行います。この段階では初期状態であることを記録します。
2. 接続(three-way handshake)
Webブラウザ
WebブラウザがSocket
ライブラリのconnect
を呼び出して、プロトコル・スタックに接続処理を依頼します。
クライアント側のプロトコル・スタック
クライアントがサーバに対して「通信しましょう!」という合図を送ります。
具体的に説明すると、クライアント側のプロトコル・スタックが「送信元と宛先のポート番号の情報」と「コントロール・ビットのSYN
を1に設定した」TCP
ヘッダーを作成し、それをサーバ側のプロトコル・スタックに送信します。
サーバ側のプロトコル・スタック
受け取ったTCP
ヘッダー内の宛先ポート番号からソケットを特定します。ソケットが特定できた場合は、そのソケットに接続動作が進行中という情報を記録します。
そのあと、「受け取りました!」という事実と「通信しましょう!」という合図をクライアントに送り返します。
具体的に説明すると、サーバ側のプロトコル・スタックが「送信元と宛先のポート番号の情報」と「コントロール・ビットのSYN
とACK
を1に設定した」TCP
ヘッダーを作成し、それをクライアント側のプロトコル・スタックに送り返します。
クライアント側のプロトコル・スタック
返ってきたTCP
ヘッダーのSYN
ビットが1であれば、ソケットにサーバ側に接続完了を示す情報を記録します。
最後にTCP
ヘッダーを受け取ったことをサーバ側に伝えるために、ACK
ビットが1のTCP
ヘッダーをサーバに送り返して、サーバがそれを受け取った時点で、接続完了となります。
3. 送信(リクエスト・メッセージの送信)
Webブラウザ
Socket
ライブラリのwrite
プログラムを呼び出して、送信データ(ここではリクエスト・メッセージ)を指定したデータ長に分割した状態でプロトコル・スタックに渡します。
クライアント側のプロトコル・スタック
MSS
(ヘッダーを除いて1つのパケットで運べるTCP
データの最大長)を超えるか、ある一定時間待った後に、バッファ・メモリー領域にたまったデータを送信するようにします。
またバッファ・メモリー領域にたまったデータがMSS
を超える場合は、MSS
のサイズに分割してデータ送信を行うようにします。
クライアント側のプロトコル・スタックは、データを分割する際にデータ断片の先頭が通信開始してから何バイト目に相当するかを数えておきます。そしてその数をシーケンス番号
としてTCP
ヘッダーに記載します。
サーバ側のプロトコル・スタック
サーバ側のプロトコル・スタックがデータ断片をしたら、シーケンス番号にデータ断片のバイト数を加算したACK
番号をTCP
ヘッダーに追加します。そうすることで、クライアント側のプロトコル・スタックに何バイト目までデータを受け取ったことを伝えます。
こうすることで、データの取りこぼしを検知することができます。例えば、ACK
番号1001までデータを受信したのに、次に受信したデータのシーケンス番号が2001だった場合、1001~2001までのデータが抜けてしまっていることにわかります。
また送信したデータに対応するACK
番号が返ってこなかった場合、送信側がデータを送り直すという強力な仕組みを備わっています。
このような仕組みによって、データを確実に通信相手に届けるようにしています。
4. 受信(レスポンス・メッセージの受信)
3.と逆になるだけなので省略
5. 切断(ソケットの抹消)
サーバ側のアプリケーション
サーバ側のアプリケーションがSocket
ライブラリのclose
を呼び出して、サーバ側のプロトコル・スタックに切断処理を依頼します。
サーバ側のプロトコル・スタック
サーバ側のプロトコル・スタックがコントロール・ビットのFIN
を1にセットしたTCP
ヘッダーを作成し、クライアントに送信します。その際に、サーバ側のソケットに切断動作に入ったという情報も記録します。
クライアント側のプロトコル・スタック
FIN
が1にセットされたTCP
ヘッダーを受けとったら、受け取ったことをサーバ側に知らせるために、ACK
番号を送り返します。
Webブラウザ
WebブラウザがSocket
ライブラリのread
プログラムを呼び出すことで、クライアント側のプロトコル・スタックにより、データを全て受信したことを把握します。それがきっかけとなって、WebブラウザがSocket
ライブラリのclose
プログラムを呼び出して、クライアント側のプロトコル・スタックに切断処理を依頼します。
クライアント側のプロトコル・スタック
クライアント側のプロトコル・スタックも、コントロール・ビットのFIN
を1にセットしたTCP
ヘッダーを作成し、サーバに送信します。すると、サーバからACK
番号が返ってくるはずなので、サーバとのやりとりが終了します。
まとめ
TCP
に則った通信では、特定のデータ長に分割したデータ断片をクライアントとサーバがお互いに確認を取り合いながら送受信を行う。途中でデータの取りこぼしなどがあったら再送信することで、確実に全てのデータを送受信できる仕組みになっている。
参考にした本
所感
TCP
は奥が深すぎます。。。今回この記事でまとめきれなかったことも数多くあります。。。ウィンドウ・サイズを使って効率的に通信を行う仕組みなどもまとめたかったのですが、時間の都合上ここまでにしました。
次回はUDP
の仕組みを紹介して、TCP
とUDP
の違いについてまとめようと思います。