はじめに
この記事では、ユーザーがブラウザにURLを入力してから、Webページが表示されるまでの裏側の世界を詳細にたどっていきます。
本記事で扱うキーワード
OSI参照モデル、TCP/IP階層モデル、HTTP、TCP、IP、ICMP、ARP、DNS
ソケット、ソケット通信、3ウェイハンドシェイク、ウィンドウ制御、再送制御、順序制御、パケットフィルタリング、フラグメンテーション、NAT、経路制御表、ポート番号、IPアドレス、MACアドレス、ヘッダ、プロトコルスタック、リゾルバ
LANドライバ、LANアダプタ、ルーター、リピータハブ、スイッチングハブ、ファイアウォール、ロードバランサー(負荷分散装置)、キャッシュサーバー、プロキシ、プロキシサーバー、ADSL、FTTH、アクセス回線、全二重通信、半二重通信
今回の記事の全体像
まず最初に、クライアント・サーバー間のデータ送受信動作の全体像を簡単にまとめたいと思います。
OSI参照モデルとTCP/IP階層モデル
OSI参照モデルは、コンピュータが通信を行うときに必要なネットワークの機能を7つの階層に分類したモデルです。今回の記事では、TCP/IP階層モデルとOSI参照モデルの対応関係を踏まえた上で、TCP/IPモデルを用いて話を進めていきたいと思います。
各階層の役割のイメージ
アプリケーションは送信したいデータを下位層に渡して通信相手に届けてもらいます。そのために、TCPは送信側と受信側にパイプのようなものを作成し、信頼性のある通信を提供します。IPはデータをパケットに分割し、最終目的地までのパケット配送を行います。ネットワークインターフェース層は、次のネットワーク機器までの中継を担当します。ハードウェアは、実際のパケットの送受信を担当します。
送受信動作の一連の流れ
下の画像はクライアント・サーバー間でのデータ送受信の全体像と、各階層でのパケット処理の様子を示したものになります。
送信側は、TCP/IP階層モデルの上位層から渡されたデータに自分の階層での処理に必要な情報である「ヘッダ」を付け加えていきます。例えば、アプリケーションから渡されたデータの前にTCPがTCPヘッダを付け加えるといったイメージです。
受信側は、受信したデータを処理して「ヘッダ」と「上位層に渡すデータ」に分離して、そのデータを上位層に渡します。TCPで「TCPヘッダ」の内容をもとに処理を行い、その後アプリケーションにデータを渡すといったイメージです。
このように、各階層で役割分担しながらデータの送受信が行われます。
全体の流れとしては、まず送信側のコンピュータでパケットが作成され、そのパケットが電気や光の信号となりコンピュータから送り出されます。そしてその信号はハブやルーターを経由した後、アクセス回線を通り、プロバイダと呼ばれるインターネット中心部を経由して、受信側のコンピュータに到着します。
以降、この送受信動作を順を追って詳細に見ていきたいと思います。まずはユーザーがブラウザのアドレスバーにURLを入力するところからです。
ユーザーがURLを入力する
ユーザーがアドレスバーにhttp://www.example.com/dir/file1.html
というURLを入力してEnterを押します。
ブラウザの送信動作(アプリケーション層)
URLの解読
ブラウザはまず入力されたURLを解析します。その結果、www.example.com
というWebサーバー上にあるdirディレクトリ下のfile1.htmlファイルにアクセスすることが分かります。
リクエストメッセージの作成
ブラウザは、これらの情報をもとにリクエストメッセージを作成します。
そして、このリクエストをOSに依頼してWebサーバーに送信してもらうのですが、その前にやることが1つあります。それは、WebサーバーのIPアドレスを調べることです。
DNSによる名前解決
URLのWebサーバーのドメイン名(www.example.com
)からIPアドレスを調べます。ここでDNS(Domain Name System)という仕組みを使用して、「www.example.com
」というサーバーのIPアドレスを調べます。DNSはドメイン名からIPアドレスへの変換、またその逆も行います。
具体的には、ブラウザがDNSリゾルバという、DNSサーバーに問い合わせを行うプログラムを呼び出します。すると、DNSリゾルバが「www.example.com
というドメイン名に対応するIPアドレスを教えて」というリクエストを作成します。そして、OSのプロトコルスタック(UDP担当部分)に依頼することでDNSサーバーにリクエストを送信してもらいます。すると、DNSサーバーはドメイン名とIPアドレスの対応表を調べてクライアントにIPアドレスを送り返します。
補足
インターネット上の数万台のDNSサーバーがIPアドレスとドメイン名の対応情報を分散管理しています。そのため、クライアントが問い合わせを行ったDNSサーバーがwww.example.comに対応するIPアドレスを知らないケースもあります。その場合は、該当DNSサーバーが他のDNSサーバーにリクエストを転送することで、最終的に目的のDNSサーバーからIPアドレスの返答を得ることができます。
リクエストメッセージの送信
HTTPリクエストメッセージを作成しWebサーバーのIPアドレスを調べた後は、リクエストメッセージの送信になります。ブラウザはソケットライブラリ内のプログラムを呼び出すことで、OSのプロトコルスタックに送信動作を行ってもらいます。ここでのクライアント・サーバー間のHTTP通信は、後述のソケットをTCPとのインターフェースとして使用することで可能になります。
プロトコルスタック:OS内部に組み込まれたネットワーク制御用ソフトウェア。
(引用元)ネットワークはなぜつながるのか 第2版 p.59
TCPの送信動作(トランスポート層)
TCPとは
TCP(Transmission Control Protocol)は、信頼性のあるデータ送受信を実現するためのコネクション型のプロトコルです。TCPの主な特徴として、順序制御と再送制御があります。
それでは、そんなTCPを使用したデータ送信動作を見ていきます。
ソケットとソケット通信
TCP/IP階層モデルのアプリケーション層のプログラム(ブラウザ)から、その下位層であるトランスポート層へのデータの出入り口となるのがソケットです。ブラウザなどのアプリケーションが各種OSで利用できるソケットライブラリ内のプログラムを呼び出すことで、ソケットが作成されソケット通信を行うことが可能になります。
TCP/IP通信を行うためにはソケットを作成する必要があります。その流れはクライアント側・サーバー側でそれぞれ以下の4つに分類できます。
ソケット通信の流れを図示すると、、
ソケット通信についてさらに詳しく見ていきます。
クライアントのデータ送受信動作
1. ソケットの作成(socket)
ブラウザがソケットライブラリのsocketを呼び出し、ソケットを作成します。この際、通信相手のIPアドレスやポート番号、通信状況などを記録するためのメモリー領域が確保されます。その後、そのソケットを識別するディスクリプタという識別子がアプリケーションに通知されます。
2. サーバー側のソケットに接続する(connect)
ブラウザがディスクリプタ、サーバーのIPアドレス、ポート番号を指定して、ソケットライブラリのconnectを呼び出します。
このとき、クライアント・サーバー間で3つのパケットが行き来し、通信に必要な制御情報(IPアドレス、ポート番号、シーケンス番号初期値など)が交換されます。そして、プロトコルスタックが相手のIPアドレス、ポート番号などの情報をソケットに記録することで、クライアント側のソケットとサーバー側のソケットが接続されます。
このように3つのパケットで制御情報をやり取りすることで、TCPコネクションが確立します。これを3ウェイハンドシェイクといいます。
3. データの送受信(write, read)
クライアント・サーバー間でソケットが接続されTCPコネクションが確立されると、ブラウザなどのアプリケーションはソケットにデータを流し込むことでデータの送受信が可能となります。
具体的には、ブラウザがディスクリプタによりソケットを指定してソケットライブラリのwriteを呼び出すことで、プロトコルスタックにデータの送信を依頼します。するとプロトコルスタックのTCP担当部分はTCPヘッダーを付加し、IP担当部分に送信動作を実行してもらいます。アプリケーションから受け取ったデータが、最大セグメント長(MSS)と呼ばれる一つのパケットで送信可能なTCPの最大データ長を超える場合には、TCPはデータを分割しそれぞれにTCPヘッダーを付加してIP部分に渡します。
実際のデータ分割の様子が以下のようになります。
MTU(1500) - IPヘッダ(20) - TCPヘッダ(20) = MSS(1460)
よりTCPの最大データ長を1460バイトとしています。
ブラウザはこのようにリクエストメッセージの送信依頼をした後に、サーバーからのレスポンスメッセージを受け取るために、ソケットライブラリのreadを呼び出しておきます。
4. ソケットを削除し切断(close)
クライアント、サーバーのどちらも先に切断フェーズに入ることができますが、今回はサーバーが先に切断フェーズに入ることとします。
サーバー側のアプリケーションがソケットライブラリのcloseを呼び出すと、サーバーのプロトコルスタックのTCP部分はTCPヘッダのコントロールビットのFINを1にセットしたTCPパケットを作成します。そして、下位層であるIPにパケットの送信を依頼し、ソケットに切断動作に入ったことを記録します。
そして、クライアントがサーバーから送信されたTCPパケット(FIN:1)を受信すると、クライアント側のプロトコルスタックは、ソケットにサーバー側が切断動作に入ったことを記録します。そして、FIN:1のパケットを無事に受け取ったことを通知するACK番号をサーバー側に送り返します。
その後、クライアント側のアプリケーションがサーバーから送られてきたデータを全て受け取ると、アプリケーションはソケットライブラリのcloseを呼び出しt切断動作を実行します。その際にクライアントは、TCPヘッダのFINを1にセットしたパケットをサーバーに送信し、サーバーからACK番号を受信することで、ソケットを削除しコネクションを切断します。
ソケットとは?
ソケットの実態は、通信相手とやり取りするための制御情報(通信相手のIPアドレスやポート番号、通信の進行状況など)、またはこれらの制御情報を記録するメモリ領域であり、物理的なものというよりは概念的なものになります。
サーバーのデータ送受信動作
サーバーアプリケーションの起動時に以下のステップ2までを行いソケットを作成しておくことで、クライアント・サーバー間でソケットを接続することができます。
1. ソケットの作成(socket)
サーバー側でもアプリケーションがsocketを呼び出すことで、ソケットが作成されます。その際にディスクリプタの値が通知されます。
2-1. ソケットの接続要求待ち(bind, listen)
次にbindを呼び出しディスクリプタで指定したソケットにポート番号を記録します。このポート番号は、Webサーバーなら80番のように事前にアプリケーション毎に決められています。そして、listenを呼び出すことでサーバー側のソケットはクライアントからの接続要求待ちの状態になります。
2-2. 接続を受け付ける(accept)
acceptを呼び出すことでクライアントからの接続要求パケットが届いた際に、接続を受け付けることができます。接続要求パケットが届くと、接続要求待ち状態のソケットをコピーして、新しいソケットを作成します。この新しいソケットにクライアントから届いた制御情報を記録することで、新しいソケットとクライアントのソケットが接続されます。
補足
接続待ちのソケットにクライアント側のソケットを接続せず、コピーした新しいソケットにクライアントを接続することで、常に接続待ちのソケットが存在することになります。これにより他のクライアントからの接続要求にも対応することができます。
3.データの送受信(read, write)
クライアント側の説明をご参照ください。
4. ソケットを削除する(close)
クライアント側の説明をご参照ください。
ポイント
クライアントとサーバーのどちらも、自身のソケットを指定する時はディスクリプタを使用し、通信相手のソケットを指定する時にはポート番号を使用します。
接続動作(3ウェイハンドシェイク)をもう少し詳しく
ソケットの接続時に、クライアント・サーバー間で行き来する3つのパケットについて見ていきます。
すでに少し触れたように、接続動作の始まりとしてクライアントからサーバーに接続要求を行います。その際に、クライアントのTCP担当部分はTCPヘッダのコントロールビットのSYNを1に設定して、コネクション確立要求のパケットを送信します。すると、サーバー側のTCP担当部分もTCPパケット(ACK番号、SYN:1)を作成しクライアントに送り返します。そして最後にクライアントからサーバーに向けて確認応答パケット(ACK番号)を送信することで、接続が完了します。
実際のパケットの流れは以下のようになります。
補足
SYN、ACK、FINなどの情報は、TCPヘッダのコントロールビットという8ビット長のフィールドにセットされます。またシーケンス番号やウィンドウサイズもTCPヘッダ内で指定されます。
TCPの順序制御と再送制御
TCPの信頼性の高い通信を実現するための機能である順序制御と再送制御について見ていきます。ここでポイントになるのが、TCPヘッダのシーケンス番号とACK番号です。
シーケンス番号
シーケンス番号は、TCPによるデータの分割が行われた際に、そのデータ断片が通信開始から数えて何バイト目かを表す値になります。受信側はこの値を使ってデータに抜けがないかを確認することができます。
受信側は受信したデータのデータサイズ+シーケンス番号
を計算することで、次に送られてくるパケットのシーケンス番号を割り出すことができます。例えば、最初に受信したパケットのシーケンス番号が1で、データーサイズが1460であれば、次のパケットのシーケンス番号は1461と計算することができます。
よって、1460バイト目までのデータを受信後に、シーケンス番号が1461のデータを受信すれば、パケットに抜けがないことを確認できますし、シーケンス番号が2921のパケットを受信した場合には、パケットに抜けがあることが分かります。
このシーケンス番号を使用することで、受信側では分割されたデータを元通りに復元することができます。(順序制御)
ACK番号
ACK番号は、パケットが問題なく届いたことを送信側に知らせるものになります。1460バイト目までを受信した場合には、ACK番号を1461にセットし確認応答パケットを送信します。もし送信したパケットのACK番号が返ってこなかった場合には、TCPはパケットを再送します。このACK番号を使用した確認応答により再送制御が可能になります。
補足
今回はシーケンス番号を1からスタートしていますが、実際には3ウェイハンドシェイク時に相手に通知したシーケンス番号の初期値からスタートします。
ウィンドウ制御方式
上記のようなTCPの1セグメントごとに確認応答(ACK番号の送信)を行なう方法だと、パケットの往復時間(ラウンドトリップ時間)が長くなり、通信性能が悪くなってしまいます。そこでウィンドウ制御方式を用いることで通信性能を向上させることができます。
具体的には、上の例のように送信したセグメントに対する確認応答を待ってから次のセグメントを送信するのではなく、複数のセグメントをまとめて送信します。そして、受信側から受信可能なデータサイズであるウィンドウサイズをTCPヘッダに記載し送信側に通知することで、受信側の受信能力を超えたパケットの送信が行われないように制御が行われます。
補足
接続動作(3ウェイハンドシェイク)のときにクライアント・サーバー間でウィンドウサイズをお互いに通知しておきます。
TCPの通信動作のまとめ(図)
IPの送信動作(インターネット層)
IPとは
インターネット層のIP(Internet Protocol)は、IPアドレスを使用して最終目的地のコンピュータまでパケットを配送します。この目的地までパケットを届けるというIPの働きによって、世界中のコンピュータと通信することができます。
実際のIPの送信動作
ここからはTCPからパケットの送信依頼を受けたIPが、どのようにパケットを送信するのか見ていきます。
IPは、TCPから渡されたTCPヘッダとデータをひとかたまりのデータとして扱います。そして、宛先IPアドレスや送信元IPアドレス等の情報を記載したIPヘッダを作成し、TCPヘッダの前に付加します。
宛先IPアドレスと送信元IPアドレス
宛先IPアドレスはアプリケーションがDNSサーバーに問い合わせを行うことで取得したサーバーのIPアドレスで、送信元IPアドレスはクライアントPCに割り当てられたIPアドレス、正確には送信元のLANアダプタのIPアドレスになります。
IPパケットが完成すると、宛先IPアドレスと経路制御表を照らし合わせて、パケットの次の中継先ルーターを決定します。
経路制御表
宛先ホストまでパケットを送るため、すべてのホストやルーターは経路制御表(ルーティングテーブル)と呼ばれる情報を持っています。この表(テーブル)には、IPパケットを次にどのルーターへ送ればよいかが記されています。
(引用元)マスタリングTCP/IP 入門編 第6版 p.144
この時点で経路制御表で調べた中継先ルーターのIPアドレスは知っていますが、MACアドレスは知りません。そこで、ARP(Address Resolution Protocol)と呼ばれるアドレス解決のプロトコルを使用することで中継先ルーターのMACアドレスを調べます。
まずは、ARPキャッシュに中継先ルーターのMACアドレスが保存されているかを調べます。保存されている場合は、そのMACアドレスを使用します。保存されていない場合には、ARPを使用してルーターのIPアドレスからMACアドレスを調べます。この中継先ルーターのMACアドレスが、後ほど作成されるMACヘッダの宛先MACアドレスになります。IPは、MACヘッダの作成をLANアダプタに依頼して行ってもらいます。
ARP(Address Resolution Protocol)
ホストA(クライアントPC)とホストB(ルーター)が同一のサブネット上に存在する場合、「170.20.1.2というIPアドレスを持っている人がいれば、MACアドレスを教えてください」という形で、ホストAがARP要求パケットをブロードキャストすることでネットワーク内のすべての機器にパケットが送信されます。そうすると、170.20.1.2というIPアドレスを持つ機器(ホストB)は、自分のMACアドレスを記載したARP応答パケットをホストAに送り返します。これにより、ホストAは宛先であるホストBのMACアドレスを知ることができます。
LANドライバの送信動作(ネットワークインターフェース層)
LANドライバは、LANアダプタを動かすためのデバイスドライバで、OS(TCP/IP)とハードウェアの仲介役となります。
IPから送信依頼されたIPパケットはLANドライバから見ると、単なるデータに過ぎません。そのため、LANドライバがIPヘッダの前にMACヘッダ(イーサネットヘッダ)を付加することにより、イーサネットのルールを使用したデータの転送が可能になります。MACヘッダには、宛先MACアドレス、送信元MACアドレス、イーサタイプなどを記載します。
宛先MACアドレスと送信元MACアドレス
宛先MACアドレスはARPで調べた中継先ルーターのMACアドレスで、送信元MACアドレスは自身のLANアダプタのMACアドレスになります。
そして、そのパケットをLANアダプタ内のバッファメモリにコピーした後、LANアダプタにパケットを送信するよう命令を出します。
IPアドレスとMACアドレス
IPアドレスはパケットの最終目的地のアドレスであるのに対し、MACアドレスはパケットの次の配送先のアドレスになります。
そのため、送信元で設定されたIPアドレスは中継先で変更されることはありませんが、MACアドレスはルーターを中継するたびに、次の中継先ルーターまたは最終目的地のコンピュータのアドレスに変更されます。
補足
これはIPが直接接続されていない機器・ネットワーク間での通信を提供するのに対し、ネットワークインターフェース層は直接接続された機器間での通信を提供するからです。
私たちが海外旅行をする例を考えると分かりやすいです。例えば、東京からシンガポールとドバイ経由でロンドンに行くとしましょう。東京を出発するときの最初の目的地はシンガポールです。そして次の目的地はドバイ、その次の目的地がロンドンとなります。このように次の目的地(MACアドレス)は毎回変わっていきますが、最終目的地のロンドン(IPアドレス)は変わることはありません。
LANアダプタの送信動作(ハードウェア)
LANドライバからフレームの送信命令を受けたLANアダプタは、デジタルデータを光や電気信号に変換してケーブルから送信します。その詳細が以下になります。
LANアダプタは、上位層であるネットワークインターフェース層からフレームを受け取ります。実際には、LANドライバがバッファメモリにコピーしたフレームを取り出しています。そして、そのフレームの先頭にプリアンブル、末尾にはFCS(フレームチェックシーケンス)を付け加えます。
プリアンブルは、フレームの読み取るタイミングとフレーム本体の開始位置を受信側に知らせるためのもので、「10101010….1011」のようなビット列になります。FCSは、フレームが送信中に破損していないかチェックするためのフィールドで、フレーム全体を特定のビット列で割った余りを格納します。送信側で計算したFCSと受信側で計算したFCSが一致すれば、フレームが正しく届いたことが確認できます。
これが実際にPCから送信されるパケットになります。LANアダプタがこのパケットを電気や光の信号に変換して、ケーブルから送信します。
PCからパケットが電気信号として出ていく
PCから電気信号となって送り出されたパケットは、リピータハブ(ハブ)やスイッチングハブ(スイッチ)を経由してルーターに向かって進んでいきます。その際の通信方式は、リピータハブを使用した半二重通信とスイッチングハブを使用した全二重通信があります。
全二重通信と半二重通信
半二重通信とは、送信をしている間は受信できず、受信をしている間は送信できないような通信のことです。...
全二重通信は、送信と受信を同時に行える通信です。
(引用元)マスタリングTCP/IP 入門編 第6版 p.101
イメージとしては、半二重通信は1本のケーブルに複数の機器が接続されているため、複数の機器が送受信を同時に行うと信号が衝突してしまいます。そのため、1つの機器のみ送信を行うことができます。
全二重通信は、スイッチのポートとそれぞれの機器を2本のケーブル(ツイストペアケーブル)で1対1に接続することで送受信を同時に行うことができます。
PCからハブに向かってパケットを送信
今回は、PC→リピータハブ→スイッチ→ルーターという順にパケットを中継して、パケットがインターネットに向かっていく様子を見ていきます。
PCがリピータハブに接続されているときは、リピータハブが全二重通信に対応していないため、半二重通信を行います。半二重通信の場合、信号の衝突を避けるため、ケーブルに他の機器が送信した信号が流れていないか確認してから、送信動作を開始します。送信動作として、LANアダプタがツイストペアケーブルから電気信号を送り出します。
リピータハブを使用せず、PCとスイッチを直接接続する場合は全二重通信を行うことが可能で、現在ではこちらの方が一般的みたいです。
ハブの中継動作
リピータハブは、TCP/IPモデルのハードウェア(OSI参照モデルの物理層)に位置し、受信した信号を接続されているケーブル(機器)全てに送信します。このように接続されている全ての機器に送信することをブロードキャストと言います。
スイッチの中継動作
スイッチは、TCP/IPモデルのネットワークインターフェース層(OSI参照モデルのデータリンク層)に位置し、MACアドレスを使って中継先にフレームを転送します。
スイッチはまず受信したパケットの宛先MACアドレスを調べることなく全パケットを受信します。
補足
スイッチのポートにはMACアドレスが割り当てられていないため、スイッチは受信したパケットのMACアドレスを調べません。
スイッチはリピータハブから送信された信号を受信すると、信号をデジタルデータに変換します。そして、フレーム末尾のFCSをチェックし、エラーがあった場合にはフレームを捨てます。エラーがなかった場合には、MACヘッダの宛先MACアドレスと転送表(フォワーディングテーブル)を照らし合わせ、宛先MACアドレスの機器が接続されているポートのみから信号を送信します。つまり、今回の例では、ハブと接続されているポート1から受信したパケットを、中継先ルーターが接続されているポート4のみから送信します。
ルーターの中継動作
以上のようにリピータハブ、スイッチを経由したパケットは、ルーターにたどり着き、そこで次のルーターへの中継動作を行います。
ルーターは、TCP/IPモデルのインターネット層(OSI参照モデルのネットワーク層)に位置し、ネットワーク間を接続して、パケットを中継をします。中継先の決定には、IPアドレスを使用します。
ルーターの受信動作
まずは受信動作です。ルーターは受信した信号をまずデジタルデータに変換します。そしてパケット末尾のFCSをチェックし、データ破損あればパケットを破棄します。データ破損がなければ、MACヘッダの宛先MACアドレスとルーターのポートのMACアドレスが一致しているか確認します。一致しない場合はパケットを破棄してしまいます。
またMACヘッダの役割はこのルーターまでパケットを届けることですから、ここでMACヘッダを捨ててしまします。
補足
ルータのポートはイーサネット、ADSL、FTTHなどの通信技術に対応しており、各ポートにはMACアドレスとIPアドレスが割り当てられています。
ルーターの送信動作
IPヘッダの宛先IPアドレスと経路制御表の対応関係を調べ、送信ポートを決定します。該当する経路が存在しない場合はデフォルトルートを採用します。
その後、経路表で調べた中継先ルーターのIPアドレスから宛先MACアドレスを調べ、MACヘッダに値をセットします。IPアドレスからMACアドレスを調べる際には、ARPキャッシュやARPを使用します。そして、送信ポートのMACアドレスをMACヘッダの送信元MACアドレスにセットします。
こうしてパケットが完成すると、パケットが信号に変換され、ルーターからスイッチ(ここではスイッチを例)に向けて送信されます。ルーター・スイッチ間では全二重通信が可能なため、そのまま信号を送信します。そしてスイッチはMACヘッダの宛先MACアドレスを見て、次の中継先ルーターに向けてパケットを送信します。これを繰り返すことでパケットは最終目的地に到着します。
ルーター(IP)のその他の機能
ルーティング(経路制御)機能
経路情報を経路制御表に追加する仕組みです。スタティックルーティングは制御表に手動で経路情報を設定する一方で、ダイナミックルーティングはBGPのようなルーティングプロトコルを使用することで、ルーター同士が自動的に経路情報を交換し制御表の設定を行います。
フラグメンテーション
送信するパケットサイズが出力ポートから送信できるパケットの最大長を超えている場合、TCPヘッダ以降のデータを分割する機能です。
アドレス変換とNAT
NAT(Network Address Translator)は、家庭や企業内などのローカルなネットワークではプライベートIPアドレスを使用して、インターネットに接続するときにグローバルIPアドレスに変換する仕組みです。NAT対応ルーターが、グローバルIPアドレスとプライベートIPアドレスの変換を行います。
パケットフィルタリング機能
MACヘッダ、IPヘッダ、TCPヘッダの内容を調べて、事前に設定した条件に一致するパケットのみ中継を行う仕組みです。これによりルーターはファイアウォールとして機能します。
通信回線の中をパケットが流れる
インターネット接続用ルーターからADSLやFTTHなどの通信回線(アクセス回線)に信号が送信され、インターネットに向かってパケットが進んでいきます。
家庭や社内のLANであればネットワーク機器間の距離はイーサネットのケーブルで直接接続できる距離ですが、最寄りの電話局や基地局までの数キロに及ぶ距離を、イーサネットのケーブルで直接接続することは現実的ではありません。そのため、ADSLやFTTHなどの通信回線を使用することで家庭や社内のLANとインターネットを接続します。
ADSL
ADSL、アナログ電話回線を拡張利用してインターネット接続を可能にするサービスで、電話とインターネット接続を同時に行うことができます。
通信回線としてADSLを使用する場合、LAN内のインターネット接続用ルーターは、3つのヘッダ(MACヘッダ、PPPoEヘッダ、PPPヘッダ)を追加したパケットをADSLモデムに送信します。ADSLモデムは受け取ったパケットを電気信号に変換し、その信号は複数の装置を経由して電柱の電話ケーブルに入っていきます。そして電話ケーブルは電話局近くで地下道に埋設され、最終的に電話局にたどり着きます。その後、電話局に設置されたADSLモデル(DSLAM)を経由してBASにつきます。BASはプロバイダのルーターと接続されているため、これでインターネットに接続することができます。
補足
ADSLモデムは、デジタルデータと電気信号の相互変換を行う装置です。
FTTH
Fiber To The Homeの略称で、その名の通り高速な光ファイバをユーザーの自宅や建物内に直接引き込む手法です。
通信回線としてFTTHを使用する場合、建物内まで光ファイバがきますが、それを直接ルーターに接続するわけではなく、ルーターと光ファイバの間にONUという装置を設置します。ADSLの時と同じく、ルーターは3つのヘッダ(MACヘッダ、PPPoEヘッダ、PPPヘッダ)を付加してからパケットを送信し、そのパケットはONUで電気信号から光信号に変換されます。そして、その光信号が光ファイバーの中を進んでいき通信会社のネットワーク内のBASまでたどりつきます。こちらの場合も同様に、BASがプロバイダのルーターと接続されているため、インターネットへの接続が可能になります。
ポイント
このようにADSLやFTTHなどのアクセス回線はユーザが契約しているプロバイダのルーターに繋がっているため、インターネットへの接続が可能になります。
BAS(ブロードバンドアクセスサーバー、BRAS)
インターネットサービスプロバイダ(ISP)との境界に設置され、インターネット接続用ルーターがユーザー認証とIPアドレスの取得を行う際の窓口役となります。ISPに接続するためのインターフェースを提供する特別なルーターです。
ルーターをインターネットと接続するためには?
ルーターからアクセス回線を経由してインターネットに接続するためには、インターネット接続用ルーターにIPアドレスを設定する必要があります。
その手順が以下になります。
- ルーターにプロバイダから割り当てられたユーザー名とパスワードを設定
- ルーターはPPPoEのDiscoveryという仕組みを使用してBASのMACアドレスを取得
- ルーターがユーザー名とパスワードをBASに送信し、認証が行われる
- 認証後、BASからユーザーに対してTCP/IPの設定情報(ルーターに割り当てるIPアドレスなど)を通知
- ルーターがこれらの設定情報をBAS側のポートに設定
これによりインターネット接続用ルーターはアクセス回線を介してインターネットに接続することが可能になります。インターネット接続用ルーターとBAS間で送信されるパケットには、MACヘッダ、PPPoEヘッダ、PPPヘッダの3つのヘッダが追加されます。
通信回線を通りパケットがプロバイダに届いた後
アクセス回線を通過したパケットは、プロバイダのPOPに設置されたルーターにたどり着きます。
クライアント側とサーバー側の契約しているプロバイダが同じ場合も異なる場合も、プロバイダ内のルーターはルーティングプロトコル(BGP)を使用して互いに経路情報を交換しあっているので、最終的にwebサーバーが接続されているPOPのルーターまでパケットが届きます。
下の図では、クライアントが契約しているプロバイダをISP①、サーバーの契約しているプロバイダをISP②としています。
POP、NOC、IXとは?
POPはプロバイダ内に存在し、複数の通信機器が接続を行うアクセスポイントです。このPOP内に設置されたルーターがインターネットへの入り口となります。
NOCはプロバイダの中心となる設備でPOPに到着したパケットが集まってきます。
IXという施設を介してプロバイダ間を相互接続することができます。
パケットがWebサーバに届くまで①(ファイアウォールの設置)
インターネットとWebサーバー側のネットワークを直接接続すると、サーバーが外部からむき出しになりセキュリティ上安全ではありません。そのため、サーバーの手前にファイアウォールを設けることが一般的です。
ファイアウォールは、事前に設定した条件に一致するパケットのみを通し、それ以外のパケットを遮断します。ファイアウォールにはいくつかの種類がありますが、ここではパケットフィルタリング型とアプリケーションゲートウェイ型について見ていきます。
パケットフィルタリング型
パケットフィルタリング型は、パケットのヘッダ情報を使用してパケットがファイアウォールを通過するか、通過しないかを決定します。MACヘッダ(宛先・送信元MACアドレス)、IPヘッダ(宛先・送信元IPアドレス)、TCPヘッダ(宛先・送信元ポート番号、コントロールビット)などのヘッダ情報を使用することで、以下のようにパケットをフィルタリングすることが可能になります。
- インターネットからWebサーバーへアクセスするパケットは通過させ、Webサーバーからインターネットにアクセスするパケットは遮断する。
- 特定のアプリケーション間(ポート番号:80など)のパケットは通過させるが、それ以外のパケットは遮断する。
パケットの通過時にはパケットを中継し、遮断時にはそこでパケットを捨ててしまいます。
アプリケーションゲートウェイ型
アプリケーションゲートウェイ型はクライアント・サーバー間の通信を仲介するプロキシという仕組みを利用します。具体的にはプロキシサーバーを設置することで、特定のURLやWebページへのアクセスを制限することが可能になります。また、プロキシサーバーはクライアント・サーバー間のやり取りを仲介するため、キャッシュの利用も可能になります。
パケットがWebサーバに届くまで②(サーバーの負荷分散)
大規模なサービスではサーバーへ大量のアクセスが行われ、1つのサーバーだけでは処理能力が追いつかないため、何かしらの方法でサーバーの負荷を減らす必要があります。その際に以下のような方法で負荷を分散することができます。
複数のサーバーを用意する方法
複数の同じ名前のWebサーバーをDNSサーバーに登録する(ラウンドロビンDNS)
名前(ドメイン名) | IPアドレス |
---|---|
www.example.com |
172.0.2.10 |
www.example.com |
172.0.2.20 |
www.example.com |
172.0.2.30 |
このように同じ名前のサーバーをDNSサーバーに登録しておきます。すると、クライアントがDNSサーバーにWebサーバーのIPアドレスを問い合わせると、DNSサーバーは順番にIPアドレスを返します。例えば、1回目の問い合わせには「172.0.2.10 172.0.2.20 172.0.2.30
」、2回目の問い合わせには「172.0.2.20 172.0.2.30 172.0.2.10
」の順番でIPアドレスを返します。これにより、複数のWebサーバーにリクエストを均等に振り分けることができます。
ただダウンしたWebサーバーのIPアドレスを返答してしまったり、同じクライアントからの一連のリクエストに異なるWebサーバーのIPアドレスを返答することでセッション維持ができないなどの問題点もあります。
ロードバランサーによる負荷分散
事前にロードバランサー(負荷分散装置)のドメイン名とIPアドレスをDNSサーバーに登録しておきます。そうすると、DNSサーバーはクライアントからの問い合わせに対して、ロードバランサーのIPアドレスを返答するため、クライアントはロードバランサーに対してリクエストを送信します。ロードバランサーは各Webサーバーの負荷状況を見ながら、負荷の少ないWebサーバーにリクエストを転送します。
ロードバランサーはダウンしているサーバーにリクエストを振り分けないようサーバーのヘルスチェックを行なっているため、ラウンドロビンDNSの欠点を克服することができます。また、同じクライアントからのリクエストを同じサーバーに転送することで、セッション維持が可能になります。
セッション維持についてもう少し詳しく
セッションの維持ができないとどのような問題が生じるのでしょうか。
例えば、ユーザーが銀行サービスで残高確認を行いたいとします。1回目のログインのリクエストがWebサーバー1に割り振られ、ユーザーはログインに成功しました。次に、「残高ページを表示してください」という2回目のリクエストがWebサーバー2に振り分けられました。すると、Webサーバー2はログインを許可していないユーザーから「残高ページを表示してください」というリクエストを受け取ることになってしまします。
この場合、ユーザーは再度ログインを求められることになるでしょう。
キャッシュサーバーを利用する方法
キャッシュサーバーはプロキシを使用して、データをキャッシュします。
キャッシュサーバーを使用する時にはロードバランサーを使用するときと同じく、キャッシュサーバーをDNSサーバーに登録しておきます。そうすることで、クライアントからのリクエストはWebサーバーではなくキャッシュサーバーに届きます。そしてリクエストメッセージの中身を調べて、そのデータが自身のキャッシュに存在するか調べます。
データがキャッシュに保存されている場合
キャッシュサーバーに保存されているデータがWebサーバー側で更新されている場合、その古いデータをそのままクライアントに返すわけにはいきません。そのため、キャッシュサーバーはクライアントから受け取ったリクエストをWebサーバーに転送し、キャッシュに保存されているデータがWebサーバー側で更新されているかどうかを調べます。「2024年4月30日以降にデータが更新されていますか?」と問い合わせるイメージです。
そうすると、Webサーバーから更新の有無を知らせるレスポンスメッセージが返ってきます。サーバー側でデータの更新がなかった場合には、キャッシュにあるデータをクライアントに送り返します。
Webサーバー側でデータの更新があった場合には、Webサーバーから最新のデータが送られてきますので、それをクライアントに送り返します。その際、キャッシュサーバーは最新のデータをキャッシュに保存します。
データがキャッシュに保存されていない場合
この場合も、キャッシュサーバーはクライアントから受け取ったリクエストをWebサーバーに転送します。その後、キャッシュサーバーはWebサーバーからのレスポンスメッセージを受け取り、それをクライアントに送り返します。そして、そのレスポンスメッセージ内のデータをキャッシュに保存します。
サーバーの受信動作
クライアントから送信されたパケットがハブ、スイッチ、ルーターなどを経由してサーバーに到着し、サーバーが受信動作を行います。その様子を見ていきます。
LANアダプタの受信動作
クライアントから送られてきたパケットが電気や光の信号となってWebサーバーに到着します。LANアダプタは、受信した信号をデジタルデータに変換します。次にフレーム末尾のFCSの値を調べることで、データ化けなどのエラーの有無をチェックします。エラーがあった場合には、そこでパケットを捨ててしまいます。
エラーがなかった場合にはMACヘッダの宛先MACアドレスがLANアダプタのMACアドレスと一致するかをチェックし、自分宛のパケットかどうかを調べます。自分宛のパケットだった場合は受信パケットをLANアダプタのバッファメモリに格納し、そうではない場合は、パケットを破棄します。
パケットの受信をCPUに通知するためには?
CPUは高速にタスクを切り替えながら様々なタスクを行っていますから、パケットの到着に気づきません。そのためCPUに受信処理を行ってもらうためには、
割り込みを使用してCPUにパケットの到着を知らせる必要があります。これにより、CPUは実行中のタスク(プロセス)を中断しLANドライバを動かすことで、受信処理が進んでいきます。
LANドライバの受信動作
LANドライバは、LANアダプタのバッファメモリからパケットを取り出します。そして、MACヘッダのタイプフィールドから上位層のプロトコルを特定し、OSのプロトコルスタックに受信パケットを渡します。今回の例では、TCP/IP部分が今後の処理を担当することになります。
IPの受信動作
プロトコルスタックのIP担当部分は、IPヘッダの宛先IPアドレスを調べ、LANアダプタのIPアドレスと一致するかを調べます。一致しない場合は、パケットを破棄しICMPパケットで通信相手にエラーを知らせます。
一致する場合は、フラグメンテーションによるパケットの分割の有無を調べます。具体的には、IPヘッダのフラグフィールドを見て分割の有無を確認します。分割が行われていた場合には、IPヘッダの識別子(ID)が一致するパケットの到着を待ち、同じくIPヘッダのフラグメントオフセットの値によりパケットを元の状態に復元(リアセンブリング)します。
最後にIPヘッダのプロトコル欄を調べ、IPヘッダの次のヘッダのプロトコルを判別します。今回はTCPで話を進めていますので、TCP担当部分にパケットを渡します。
ICMP
ICMPは、IPで発生した異常を知らせるプロトコルです。
例えば、宛先IPアドレスと自分のIPアドレスが一致しなかった場合、つまりIPパケットが最終目的地までたどり着かなかった場合、通信相手にICMPパケットを送信することで、どのようなエラーが発生したのか知らせることができます。
ICMPのメッセージは大きく以下の2つに分類できます。
- エラー通知のためのエラーメッセージ
- 診断などを行う問い合わせメッセージ
TCPの受信動作
TCPの受信動作は届いたパケットの種類によって異なります。
接続要求パケットの受信時
TCPヘッダのコントロールビットのSYNが1の時は、接続動作を行います。
ヘッダの宛先ポート番号と同じポート番号を持つ接続待ちのソケットが存在しない場合は、ICMPパケットでクライアントにエラーを通知します。
存在する場合は、そのソケットのコピーを新たに作成し、そこに通信を行うための制御情報(IPアドレス、ポート番号など)を記録します。このとき、ACK番号やSYN、そして、シーケンス番号の初期値を設定したTCPヘッダを作成し、クライアントに送り返します。
データパケットの受信時
TCPヘッダの宛先・送信元IPアドレス、宛先・送信元ポート番号の4つが一致するソケットを特定します。ソケットの通信状況の記録から次のパケットのシーケンス番号(前のパケットのシーケンス番号+データ長)を算出し、届いたパケットのシーケンス番号と一致するかを調べます。これによりパケットの損失をチェックすることができます。
TCPはパケットからアプリケーションデータを取り出して、受信バッファに保存します。TCPはアプリケーションのデータがMSSを超える場合には、データを分割して通信相手に送信します。そのため、データが分割されていた場合にはシーケンス番号を使用することで、受信バッファ内でデータを正しい順序に戻し、元の状態に復元します。
そして、TCPヘッダにACK番号をセットした確認応答パケットをクライアントに送信します。
アプリケーションの受信動作
アプリケーションは、ソケットライブラリのread()を呼び出すことで受信バッファのデータ(リクエストメッセージ)を取得します。そして、その内容を調べて適切な処理を行います。
URIがHTMLや画像ファイルへのパスのとき
GETメソッドでHTMLや画像ファイルの取得を要求するリクエストが届いた際には、レスポンスメッセージのメッセージボディにデータを入れてクライアントに送り返します。
プログラムへのパスのとき
URIで指定するパスは、HTMLや画像ファイルへのパスだけではありません。サーバー側のプログラムへのパスを設定することもあります。その場合、プログラムの処理結果をレスポンスメッセージでクライアントに送り返します。
具体的な流れを下の画像のログインの例とともに見てみます。ブラウザでユーザーがusernameとpasswordを入力しログインボタンを押した後、リクエストメッセージがWebサーバーに送られてきます。すると、Webサーバーは/loginにURLマッピングされたプログラムを起動し、リクエストメッセージから取り出したusernameとpasswordデータをログインプログラムに渡します。
それをもとにログインプログラムがログイン処理を行い、処理結果をWebサーバーに渡します。
その後、Webサーバーがディスクリプタと送信データを指定してソケットライブラリのwrite()を呼び出すことで、プロトコルスタックがレスポンスメッセージを作成してクライアントに送信してくれます。
レスポンスメッセージが、ハブ、スイッチ、ルーターなどを経由してインターネットの中を通り、クライアントに到着します。この流れはクライアントからサーバーにデータを送信する時と同じになります。
クライアントがレスポンスメッセージを受信
クライアントの受信動作は、Webサーバーの受信動作と同じです。
まず、LANアダプタがWebサーバーからの返答パケットを受信し、デジタルデータに変換後にバッファメモリに格納します。その後、LANドライバがバッファメモリから受信パケットを取り出します。その際にイーサネットフレームのタイプフィールドの値を調べて、上位層のプロトコルスタックにパケットを渡します。ここでは、TCP/IPのプロトコルスタックにパケットを渡します。
すると、プロトコルスタックのIP担当部分は、IPヘッダの宛先IPアドレスをチェックし、自分宛のパケットかどうかを調べます。自分宛のパケットでなかった場合はパケットを破棄してICMPパケットを送信し、自分宛だった場合はフラグメンテーションによるパケットの分割の有無を調べます。パケットが分割されていた場合には、パケットを分割前の状態に復元します。最後にIPヘッダのプロトコル欄を調べ、IPヘッダの次のヘッダのプロトコルを判別します。
IP担当部分が以上の仕事を終えると、TCPにパケットを渡します。TCPはIPヘッダの宛先IPアドレス、送信元IPアドレス、TCPヘッダの宛先ポート番号、送信元ポート番号の4つの値を調べ、一致するソケットを特定します。そして、ソケットの通信の進行状況の記録をもとに適切な処理を行います。接続や切断用のパケットであれば、それ用のTCPパケットや確認応答パケットを送信しますし、データパケットであれば受信したデータを受信バッファに保存していき、アプリケーションがデータを取りに来るのを待ちます。
ブラウザがレスポンスメッセージを表示
ブラウザがread()を呼び出すことでレスポンスメッセージを取得します。その後はレスポンスメッセージをブラウザ画面に表示する、レンダリングが行われます。
ブラウザのレンダリング動作
ブラウザはレスポンスメッセージを解析し、Webページを画面に表示します。今回は、メッセージヘッダの値からメッセージボディの中身がHTMLであることが分かります。
Content-Type: text/html; charset=UTF-8
ブラウザはHTMLファイルを上から順に解析し、divやtableなどのタグの意味を解釈することでページレイアウトを行います。その際に、imageタグなどの外部リソースを見つけたら、再度サーバーにリクエストを送信することで該当データを取得します。
imageタグは画像埋め込み用のタグなので、再度リクエストを送信することで画像データを取得します。そして、imageタグがあった場所に画像データを表示させます。このとき実際の画面表示動作はOSに行ってもらいます。
これにより、Webページがブラウザ画面に表示されます。
おわりに
かなりボリュームのある内容になりましたが、以上になります!今後も必要に応じて修正・更新を行っていきたいと思います。