🎃

URLが入力されて画面に表示されるまで ( 1 )

に公開

はじめに

日頃の業務ではあまり意識はしていなかった、ブラウザにURLを入力してブラウザに画面が表示されるまで、どのようなことが裏側で起こっているのかが気になったので概要をまとめました。
私自身ネットワークに関しての知識はほとんどありませんが、ネットワークの理解を深めるために記事を書きます。私のような初学者に少しでも参考になれば幸いです。

ブラウザがURLを解読する

まず初めに、このようなURLをブラウザに入力したとします。するとブラウザは(a)のように解読します。

http://hoge.com/dir/index.html

(a)「http:」+「//」+「hoge.com」+「/」+「dir」+「/」+「index.html」
解読した結果以下のことが分かります。

  • プロトコル : http
  • Webサーバー名 : hoge.com
  • パス : 「/」+「dir」+「/」+「index.html」

つまり、HTTPプロトコルを使って、hoge.com サーバーの/dir/index.htmlにアクセスするという意味を持ちます。その後、ブラウザはHTTPリクエストメッセージを作成します。

HTTPリクエストメッセージの構成

HTTPリクエストは、通常以下の要素で構成されます。

  • リクエストライン
  • ヘッダーフィールド
  • メッセージボディ

HTTPリクエストメッセージの例

http://hoge.com/dir/index.html へのアクセスの場合、ブラウザがhoge.comサーバーへ送信するリクエストは次の形式になります。

GET /dir/index.html HTTP/1.1
Host: hoge.com
User-Agent: Mozilla/5.0 //省略
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,//省略
Accept-Encoding: gzip, deflate, br
Accept-Language: ja,en-US;q=0.9,en;q=0.8
Connection: keep-alive

それぞれ以下のように対応しています。

  • リクエストライン : GET /dir/index.html HTTP/1.1
  • ヘッダーフィールド : Host ~ Connection
  • メッセージボディ : GETリクエストの場合、通常メッセージボディは含まれません。もしフォームの入力データなどを送信するPOSTリクエストであれば、ここにデータが格納されます。

ドメイン名の解決

ブラウザは、HTTPリクエストメッセージを送信する機能は持っていないため、OSに依頼する必要があります。OSに依頼する際に、送信先のIPアドレスが必要なため、HTTPリクエストメッセージを作成した後は、URLの中のドメイン名からIPアドレス(注釈1)を調べます。この変換プロセスが「ドメイン名の解決」、具体的にはDNSを利用したお問い合わせです。では、どのようにしてIPアドレスを調べるのでしょうか。それは、リゾルバと呼ばれる機能を利用してDNSサーバーに問い合わせをします。そして、この通信の裏側には、ソケットという仕組みが隠されています。

ドメイン名解決の具体的な流れ(DNSお問い合わせ)

  • ブラウザからSocketライブラリを介してOSへ依頼
    • ブラウザは、ドメイン名の解決のためにOSが提供する機能を利用しますが、直接機能を呼び出すわけではありません。代わりにSocketライブラリを通じでドメイン名解決の関数を呼び出します。
    • この際ブラウザは、hoge.comのIPアドレスを知りたい旨をSocketライブラリに伝えます。
  • SocketライブラリからOSのリゾルバへの橋渡し
    • Socketライブラリの内部では、このドメイン名解決の要求を受け取ると、OSが提供する「リゾルバ」機能を呼び出します。このリゾルバは、アプリケーションからのドメイン名解決要求を受け付け、実際のDNS問い合わせを行うプログラム群です。リゾルバは、DNSサーバーに問い合わせるためのメッセージを作成します。
  • OSからSocketライブラリを介してブラウザへの通知
    • メッセージの送信は、OS内部に組み込まれたネットワーク制御ソフトウェアの、プロトコル・スタックが行います。DNSサーバーが応答メッセージを送信し、プロトコル・スタックが応答メッセージを受け取り、リゾルバに受け渡す。リゾルバは、応答メッセージを元にIPアドレスを解読しブラウザに返します。

データ送信の依頼

ここまでで送信先のIPアドレスが判明しました。しかし、ブラウザは直接データをネットワークに流すわけではありません。メッセージを送信するようにプロトコル・スタックに依頼をします。
この「依頼」の具体的な窓口となるのがソケットです。ソケットは、アプリケーション(この場合はブラウザ)とOSのプロトコル・スタックがネットワーク通信を行うための、ソフトウェア的な窓口として機能します。「電話機」のようなものです。

ソケットの作成、接続、送受信、切断

  • ソケットの作成(socket())
    • まず、ブラウザは通信を行うための「窓口」となるソケットをOSに作成してもらいます。これは、Socketライブラリのsocket()関数を呼び出すことで行われます。このsocket()関数を呼び出す際、ブラウザはどのような種類のソケットが欲しいかをOSに伝えます。socket()関数が成功すると、OSは作成したソケットを一意に識別するための「ソケットディスクリプタ(Socket Descriptor)」をブラウザに返します。このディスクリプタは、ブラウザが今後このソケットを通じて通信を行う際の識別子となります。この時点では、ソケットはただ作成されただけで、まだ特定のサーバーへの接続は行われていません。
  • サーバーへの接続(connect())
    • ソケットができたら、次にそのソケットを使って目的のウェブサーバーに接続を試みます。これを行うのが、connect()関数です。OSはソケットを通じてこの要求を受け取り、その内部にあるプロトコル・スタックに接続処理を依頼します。
  • データの送信(send()/write())
    • コネクションが確立されたら、ブラウザは準備していたHTTPリクエストメッセージをサーバーに送ることができます。これにはsend()関数(またはwrite()関数など)を使います。
      send()を呼び出すと、ブラウザが指定したソケットディスクリプタを通じて、メッセージがOSのプロトコル・スタックに渡されます。プロトコル・スタックは、メッセージをネットワークで送れる形(パケット)(注釈2)に変換し、サーバーへ送信します。サーバーからのレスポンスも、このソケットを通じてブラウザに届きます。
  • コネクションの切断 (close())
    • 通信が終わってソケットが不要になったら、安全にコネクションを切断します。これにはclose()関数close()を呼び出すと、OSのプロトコル・スタックはサーバーと「4ウェイハンドシェイク」という手順を行い、双方向の通信を完全に終了させます。これにより、使っていたネットワークリソースが解放されます。

どのようにデータが運ばれるのか

URLを解読 → ドメイン名を解決 → ソケットを作成してデータを運び、ソケットを切断まで見て行きました。これからは、具体的にどのようにデータが運ばれるのか、ネットワーク制御ソフトウェアの、プロトコル・スタックを詳しく見て行きます。

TCP/IPとは

TCP/IPとは、通信プロトコル群の総称です。その名の通り、主に「TCP(Transmission Control Protocol)」と「IP(Internet Protocol)」という二つのプロトコルが中心となり、互いに連携して機能します。これらはプロトコル・スタックの主要な部分を構成しています。

TCP

TCPは、IPが運ぶデータが、順番通りに相手に届くことを保証する役割を担います。ブラウザがウェブサーバーに接続を要求すると、OSが提供するSocketを通じてやり取りが行われます。

接続の確立: connect() 3ウェイハンドシェイク

例えば、connect()が呼び出されると、OS内のTCPプロトコルが、以下の「3ウェイハンドシェイク」という手順を実行して、サーバーとの間で安全な通信路を確立します。
具体的にTCPは、サーバーに対して「接続を開始したい」という意図を伝えるSYN(Synchronize)という制御情報を持つパケットを送信します。このパケットには、シーケンス番号が含まれています。

  • シーケンス番号とは
    TCPには、送信相手にパケットが届いているかを確認し、届いていない場合再送信する機能を持っています。TCPはアプリケーションのデータを分割しその断面から断面までのバイトを見ます。このバイトがシーケンス番号です。先頭から数えて30バイト目、100バイト目のような感じです。

SYN-ACKパケットの受信と送信

サーバー側のTCPがSYNパケットを受信すると、シーケンス番号を確認します。例えば、30バイト目で受信し終わった状態で、シーケンス番号が31だとデータが全て送られたことがサーバー側が判断できます。 サーバー側は、そのSYNパケットへの確認応答とともに、「自分も接続の準備ができた」という意図を示すSYNを合わせたSYN-ACKパケットをクライアントに返します。このパケットには、サーバー側のシーケンス番号の初期値も含まれます。

ACKパケットの送信

クライアント側のTCPがSYN-ACKパケットを受信すると、サーバーのSYNに対する確認応答(ACK)パケットを送信します。

IP

IPは、IPアドレスに基づいて、データを正しい宛先に届ける役割を担います。
例えるなら、郵便物の「住所」と、郵便物を「最適な経路で目的地まで運ぶ」ための道案内役です。IPは個々のパケットがどこからどこへ行くべきかを決定します。

  • 主な役割
    • アドレス指定(IPアドレス)
      • インターネットに接続されたすべてのデバイス(コンピューター、サーバー、ルーターなど)には、一意のIPアドレスが割り当てられます。IPはこのIPアドレスを使って、データの送信元と宛先を識別します。
    • IPは、送られてきたパケットの宛先IPアドレスを見て、ネットワーク上のどの経路を通って送るべきかを判断します。インターネット上には多数のルーターが存在し、IPはこれらのルーターと連携して、パケットを最適な経路で次々と転送していきます。この過程を「ルーティング」と呼びます。

データはパケットという小さい塊で運ばれる

インターネットを含むネットワークでは、大きなデータを一度に送るのではなく、小さな塊に分割して送ります。この小さなデータの塊を「パケット」と呼びます。IPが主に扱うデータの単位がこのパケットです。データを分割するには理由があります。一度に巨大なデータを送ると、ネットワーク回線を長時間占有してしまい、他の通信の妨げになる可能性があります。また、途中でエラーが起きた場合に全てを再送しなければならず非効率なためです。
また、パケットはただのデータの塊ではありません。それぞれのパケットには、「どこから来て、どこへ行くのか」「この荷物の種類は何か」といった情報が書かれた「荷札」が付けられています。IPがパケットに付加するこの荷札にあたる部分が「IPヘッダー」です。
送信元IPアドレスや宛先IPアドレスが含まれています。

まとめ

以下のような流れでデータリンク層まで届きます
1.アプリケーション層(例: HTTP)
ブラウザがHTTPリクエストメッセージを作成します。
例: GET /index.html HTTP/1.1 に、必要なヘッダー(Host, User-Agentなど)が付いた状態。ブラウザはURLを解読し、Socketを通じてOSにあるリゾルバにドメイン名からIPアドレスを調べる名前解決を行います。名前解決が完了し、IPアドレスが分かったら、ブラウザはHTTPリクエストを準備し、Socketライブラリのsend()関数などを使ってOSにデータ送信を依頼します。

2.トランスポート層(例: TCP)
OSのプロトコル・スタックのTCP部分が、ブラウザから受け取ったHTTPリクエストデータにTCPヘッダーを付加します。これには、送信元・宛先のポート番号、シーケンス番号、確認応答番号(SYN, ACKなど)が含まれます。この時点では、TCPヘッダー+HTTPデータ = 「TCPセグメント」となります。

3.インターネット層(例: IP)
TCPセグメントは、IPプロトコルに渡されます。IPは、このTCPセグメント全体をデータとして扱い、IPヘッダーを付加します。IPヘッダーには、送信元・宛先のIPアドレス、などが含まれます。この時点では、IPヘッダー+TCPセグメント = 「IPパケット」となります。

4.データリンク層(例: イーサネット)
IPパケットは、さらに下位のデータリンク層に渡されます。データリンク層では、MACアドレス(Media Access Control Address)という固有の識別子に基づいたヘッダーと、データの完全性を確認するためのフッターが付加されます。これにより、IPパケットはカプセル化されます。このカプセルを、物理層に渡され、電気信号や光信号、電波などに変換されて、LANケーブルやWi-Fiを通じて実際にネットワーク上を伝送されます。(データリンク層は次節で書く予定です。)

最後に

今まで裏側で起こっていることを知らずに日々業務をしていましたが、ネットワークを知ることで仕組みを少しは自分で想像できるようになりました。
次節では、データリンク層の説明を書き、記事は今後もアップデートしていく予定です。

補足

IPアドレスとは (注釈1)

IPアドレス(Internet Protocol Address)とは、インターネットに接続されたコンピューターやネットワーク機器それぞれに割り当てられる、識別番号のことです。インターネット上での「住所」のようなもので、データがどの機器に送られるべきかを特定するために使われます。役割は、機器の識別やデータのルーティングを行います。192.168.1.1のように8ビットの数字が4つ、ドットで区切られた形式のIPv4, 2001:0db8:85a3:0000:0000:8a2e:0370:7334のように、16ビットの数字が8つ、コロンで区切られた形式 IPv6があります。

参考資料

https://bookplus.nikkei.com/atcl/catalog/07/P83110/

Discussion