to trip page
myself

八谷航太

18yo / ソフトウェア開発者

iconiconiconicon

TCPが繋がって切れるまで

2020-10-22 計算機科学

最近ふと、「あれ?俺TCP通信理解してなくね?」と思ったので軽くTCPの働きをまとめておきます

ソケットを作る

アプリケーションでSocketライブラリが呼び出されたタイミングでソケット一つ分のメモリ領域を確保し、制御情報の初期値を記録する。この時点でソケットが一つできたことになるので、このソケットのディスクリプタをアプリケーションに返す。今後アプリケーションがプロトコルスタックにデータ送受信動作を依頼する際にディスクリプタを渡すことで、いちいちアプリケーションが情報を渡す手間を省くことができる。アプリケーションはこのディスクリプタと名前解決で得たサーバーのIPアドレス、IPアドレス内での接続先を示すポート番号を引数にconnect()メソッドを呼び出し、接続フェーズに入る。

サーバーと接続する

宛先情報を引数として受け取ったTCPはTCPヘッダーのSYNビットを1にし、ウィンドウサイズやシーケンス番号を記載してサーバーに送信する。サーバー側のTCPは宛先のソケットに接続したらSYN・ACKビットを1にして、サーバー側のウィンドウサイズと乱数で算出したシーケンス番号を記載したTCPヘッダーをつけてクライアントに送り返す。それを受け取ったクライアントのTCPはソケットに制御情報を追加し、再びACKビットを1にしてサーバーに受信確認を送る。この接続動作ではクライアントとサーバー合わせて3回のやりとりで接続が完了するため「3ウェイ・ハンドシェイク」と呼ばれている。

データを送受信する

接続が完了したらいよいよデータの送受信に入るが、TCPはMSSに近づくまで送信ようバッファメモリにデータをため、ある程度まで貯まった段階で送信動作に入る。ただ、中々MSSに近づかない場合は送信動作の遅延を招くので、一定時間が経過したら強制的に送信動作が実行される仕様になっているプロトコルスタックがほとんどである。クライアントのTCPはデータを送信する際にシーケンス番号をTCPヘッダーに記載し、サーバーのTCPは受け取ったデータのTCPヘッダーを確認してシーケンス番号+データのバイト数であるACK番号をクライアントに返す。こうすることでクライアントはサーバーが正常にデータを受け取ったかを確認することができる。

この時、サーバーからACK番号が帰ってくるのをいちいちクライアントが待っていたら通信速度はえげつなく遅くなってしまう。そのため、実際の通信では、接続フェーズで受信側のウィンドウサイズ、つまり受信バッファの空き容量を相手に伝えて置くことでウィンドウサイズに到達するまではACK番号を待つことなくデータを送信できる仕組みになっている。受信側は受信処理が完了して新たにバッファの空き容量が変化したらTCPヘッダーのウィンドウフィールドでウィンドウサイズを通知する。

サーバーから切断する

ブラウザの場合はサーバーが最後のレスポンスデータを返してデータ送受信が終了するのでサーバー側から切断動作に入るが、クライアントから切断するアプリケーションも存在する。

サーバーは最後のレスポンスメッセージを送信したら、TCPヘッダーのFINビットを1にしたデータをクライアントに送信し、ソケットに切断動作に入ったという情報を記載する。FINビットが1のデータを受け取ったクライアントは同様にソケットに切断動作に入ったという情報を記載し、サーバーにACK番号を返す。その後アプリケーションにデータ受信が終了したという情報を伝え、今度はクライアントからFINビットを1にしたデータをサーバーに送信する。それを受け取ったサーバーはACK番号をクライアントに返し、切断が完了する。


あくまでこの記事はまとめなので実際にデータ送受信の際はもっと他の動作も行われていますが、接続から切断までクライアントとサーバーのTCPさんが行っている仕事を大まかに説明するとこんな感じになります。