ネットワークはなぜつながるのか　自分の言葉で備忘録

# 第1章 Webブラウザがメッセージを作る

ブラウザでURLバーにアドレスを打ってから何が起こるのか

1. まずはURLの解析が行われる
http://www.hogehoge.com/fuga

というURLがある
これは

http => プロトコル
www.hogehoge.com => サーバー名
/fuga => パス

という大まかに分けて3つに分類される

プロトコルは通信の方式
サーバー名はどのサーバーか
パスはサーバーの中のどのファイルorディレクトリか
を表す

次にブラウザはHTTPプロトコルを使用することがわかったのでHTTPリクエストを組み立てる

HTTPリクエストは以下の4つからなる

- リクエストライン
    - メソッド
        - 大まかにわけて以下の2つがある
            - 取得するGET
            - 送信するPOST
    - URI
        - Webサーバーで動作するプログラムのファイル名
    - HTTPバージョン
        - その名の通り
- メッセージボディ
    - メッセージの内容

そしてサーバーにこのリクエストを送るとサーバーからはレスポンスが返ってくる

- レスポンスライン
    - HTTPバージョン
        - その名の通り
    - ステータスコード
        - どんな状態かを端的に表す
    - レスポンスフレーズ
        - ステータスコードの内容を表す
- メッセージボディ
    - - メッセージの内容

ちなみに画像などがwebページに100個あったらこれらのリクエストは個別にサーバーに100個送られて個別にレスポンスが返ってくる

HTTPについてはこれでおしまい
つぎはサーバーに対してどうやってデータを送るか
サーバーに対してデータを送るにはIPアドレスというものが必要になる

IPアドレスとはPCの住所のようなもので
172.11.34.11みたいな `.`が3つつくもの
各`.`で繋がれた数字はすべて0~255の数字になる

アドレスを入力されたブラウザはどうやってhogehoge.comという文字列からこのIPアドレスを取得しているか？

それはDNSサーバーというPCの住所を知っているサーバーに対して「こいつのIPアドレスはなんですか？」と訪ねて、もしそのDNSサーバーが知っていたら「こいつのIPアドレスはXXXX.XXXX.XXXX.XXXXですよ〜」と教えてくれる

これをDNSルックアップという

仕組みはシンプルで以下のような対応表が各DNSサーバーにあってサーバー名が各PCからわたってくるのでそれに対して一致するものがあれば返答してくれる

| サーバー名             | タイプ | クライアントに返答する項目 | 
| ---------------- | ------ | -------------------------- | 
| www.hogehoge.com | A      | 123.234.211.111            | 
| www.google.com   | A      | 142.251.42.142             |  

ちなみにターミナルで以下のように打つと
```
nslookup google.com
```

返ってくる
```
Non-authoritative answer:
Name:	google.com
Address: 142.251.42.142
```

そして普通に`142.251.42.142`をアドレスに打ち込むと普通にgoogleが表示される

余談
- DNSサーバーに尋ねるにはブラウザからは直接できないらしくてDNSリゾルバというブラウザとは別の仕組みを使用して尋ねる
- そのDNSリゾルバも実はただ実行するだけのものらしくてプロトコル・スタックという通信するためのものに頼む

DNSサーバーには対応表があってリクエストに一致するものがあれば返されるっていったけどないときはどうするねんってなると思うけどその時はDNSサーバーの中で一番上位の存在のルート・ドメインっていうDNSサーバーに聞くと解決する

こんな感じ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/660329/22557783-d3e0-e898-4ece-2a30d0ea7d32.png)

DNSサーバーは階層構造になっていて

`www.hogehoge.com.jp`というドメインがあるとして
wwwはhogehogeに属していて
hogehogeはcomに属していて
comはjpに属しているとみる

一番右のやつが一番でかくて左に行くにつれてどんどん規模が小さくなっていく
んで
ルートはcomとかjpとかukとかいうドメインを束ねている
jpはcomとかtokyoとかそういったドメインを束ねている
そのjpの中のcomの中のhogehogeの中にwwwというサーバーの実態がある

それでルートに聞くとまずjpにきいて、jpはcomに聞いてcomはhogehogeに聞いて結局hogehogeが「にwwwという実態がありましたー。こいつのIPアドレスはXXXX.XXXXX.XXXX.XXXXですー」っていう返答を返してくれる

ちなみにこんな感じで聞いていったけど結局どこにもなかったという場合も多々あり、そういうときは `DNS_PROBE_FINISHED_NXDOMAIN` という返答が返ってくる

実際に
http://fjwlekjfwekl.com/ という適当なURLを打ったらこんな感じの画面になる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/660329/1459215a-76ec-2126-ad7d-00fc512eabb5.png)

IPアドレスを調べることができたらやっとそのIPアドレスの相手(webサーバー)に対してHTTPリクエストを送ることができるようになる

(ただHTTPリクエストを呼ぶときもDNSのリクエストを送るみたいにプロトコル・スタックというハードウェア寄りの機構を呼ぶことになる)

HTTPリクエストとHTTPレスポンスをブラウザとサーバーの間で行ったり来たりさせるためにはブラウザとサーバーの間でデータの通り道を用意する必要がある

# 第2章 TCP/IPのデータを電気信号にして送る
第1章でURLを解読し、HTTPリクエストを作り、ドメイン名からIPアドレスを取得するDNSルックアップまでを勉強したが、この章ではその先の動きを勉強する

HTTPリクエストをサーバーに送るため、必要なのはTCPプロトコルを使用してサーバー側と通信のパイプを確立しデータを送受信すること

そのために必要なステップは以下の3つ
1. ソケットを作成する
2. サーバーに接続する
3. データを送受信すること

### 1. ソケットを作成する
TCPプロトコルは接続のための制御をするプロトコルでブラウザ <=> サーバーの通信ではこちらが主に使用される

ソケットを作成すると通信を制御するための情報として、通信相手のIPアドレスやポート番号、通信の進行状態などの情報が格納されている

### 2. サーバーに接続する
クライアントから 「IPアドレス、ポート番号、何をしたいか」といった情報を受け取ると接続が確立される

また
> 「IPアドレス、ポート番号、何をしたいか」
このような送受信の際に使用される情報はTCPヘッダーといい、以下のような種類がある

| 項目               | 長さ     | 詳細                                                                             | 
| ------------------ | -------- | -------------------------------------------------------------------------------- | 
| 送信元ポート番号   | 16ビット | 送信した側のプログラムのポート番号                                               | 
| 宛先ポート番号     | 16ビット | 送信先のプログラムのポート番号                                                   | 
| シーケンス番号     | 32ビット | このパケットの先頭位置のデータが送信データの何倍と目に当たるのか伝えるためのもの | 
| ACK番号            | 32ビット | データが何バイト目まで受信側に届いたのか伝えるもの                               | 
| データオフセット   | 4ビット  | ヘッダーの長さを表す                                                             | 
| コントロールビット | 6ビット  | 制御用のもの                                                                     | 
| ウィンドウ         | 16ビット | 受信確認を待たずにまとめて送信可能なデータ量を通知するためにしようする           | 
| チェックサム       | 16ビット | 誤りの有無を検査するためのもの                                                   | 

クライアント側のTCPヘッダーのコントロールビットのSYNというビットを1にしてサーバーにパケットを送る


サーバー側がそのTCPヘッダーを受け取り、そこに記録されている宛先ポート番号と同じソケットを探したら、必要な情報を記録し、コントロールビットのSYNとACKを1にしたTCPヘッダーを作り、パケットをクライアントに送り返す。

接続を終了する際は送り返されたクライアントがコントロールビットのACKを1にしてサーバーに送る

### 3. データの送受信
データが送り返されたときにすぐにデータを送るのではなく、ある程度データが溜まった状態でパケットを送る
なぜならすぐに送ると小さなパケットをたくさん送られてしまい、ネットワークの利用効率が低下してしまうため。

また1つのパケットで送ることができるバイト数は決まっていて、1500バイトとなっている
もしデータが1500バイトを超えると分割して送ることになる

- UTF-8では半角英数字は1文字1バイトなので1500文字

分割するときにどこが何番目かという情報が必要になるので(=6000バイトあるとして4つに分割されたときに送った順番がないとぐちゃぐちゃになる)TCPヘッダーにあるシーケンス番号という項目にそのパケットが先頭から数えて何バイト目かの値を格納して送ることで受け取り側が順番を知ることができる

受け取り側(サーバー)はそれ以前のデータと合わせて何バイト目までを受け取ったかを計算してTCPヘッダーのACK番号に記載して送信側(クライアント)に知らせます

### 制御方式について
パケットを1つ送ってACKを待つ(=`ピンポン方式`)というのはシンプルですが、その時間そのまま待つのは時間がもったいないため、クライアントがACKを待たずにどんどんデータ送信をする`ウィンドウ制御方式`というものがあります

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/660329/73643987-4438-cc49-562e-5ddfd36ad63b.png)

こんなイメージです
受け取り側は送られたパケットを受信したらとりあえずメモリーにデータを仮置きします
仮置きされたデータの断片をつなぎ合わせて元のデータを復元してアプリケーションに渡します

ただもしクライアントがこのメモリーの許容量を超えてパケットを送ってしまったら溢れてしまいます

そこで受信側はクライアントに対して事前にメモリーのキャパシティを送ることでクライアントは送っても良いバイト数を知り、そのキャパシティまでをデータを送ることができます
このバイト数はTCPヘッダーの`ウィンドウフィールド`に格納されてクライアントが知ることができます
またこの許容量を`ウィンドウサイズ`といいます

### パケットの基本
サーバーに送られるためにパケットには宛先アドレスからXXX番目のケーブルが使用されるということがわかり、そこから送信される。送信されたあとに中継装置(ルーター)に届き、さらにその次の中継装置に届く
具体的にすると
1. ルーター(IP担当)が目的地を見定めて次のルーターを示し
2. ハブ（イーサネット担当）がサブネットの中でパケットを運んで次のルーターに届ける

パケットが荷物だとすると、ルーター(IP)は空港で飛行機が飛んで、ハブ(イーサネット)はバスターミナルで、バスが出発して国の中のどこかに届けられるイメージ
国の中の空港にはバスで移動し、国から国(もしくは遠い国内の空港)へは飛行機で移動する

#### パケットのイメージ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/660329/be025a33-76c4-88dc-df5f-7c16a35d851c.png)
#### ハブのイメージ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/660329/d716e8c0-af16-90f5-0929-d5d36c624b0d.png)
#### ルーターのイメージ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/660329/adad37b4-d6dc-7c16-99c7-341578174202.png)

TCPからデータを送信することを依頼されたIP担当部分は
MACヘッダーとIPヘッダーとを作ってTCPヘッダーの前にくっつけます

IPヘッダーは以下です(全部ではない)

| 項目             | サイズ     | 詳細                                                                                                                         | 
| ---------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------- | 
| 送信元IPアドレス | 32バイト | 発信した側のIPアドレス                                                                                                       | 
| 宛先IPアドレス   | 32バイト | 届ける相手のIPアドレス                                                                                                       | 
| ヘッダー長       | 4バイト  | IPヘッダーの長さ                                                                                                             | 
| ID情報           | 16バイト | ここのパケットを識別する番号。<br>分割されたパケットはすべて同じ番号になる                                                   | 
| 生存期限(TTL)    | 8バイト  | ネットワークにループができたときに永遠にパケットが回り続けないようにするためのもの。<br>ルーターを経由するたびに値が-1される | 
| プロトコル番号   | 8バイト  | TCP: 06<br>UDP: 11<br>ICMP: 01 |
| フラグ           | 3バイト  | フラグメンテーションの可否とこのパケットが分割されたかどうかを表す                                                           | 

宛先IPアドレスはTCPから伝わってきたもの(DNSルックアップをして得られたアドレス)で送信元IPアドレスはクライアントのLANアダプタのアドレス
※IPアドレスはコンピュータに対して割り当てられるのではなく、LANアダプタに割り振られる

#### MACヘッダー
| 項目              | サイズ   | 詳細                                    | 
| ----------------- | -------- | --------------------------------------- | 
| 宛先MACアドレス   | 48バイト | パケットを届ける相手のMACアドレス       | 
| 送信元MACアドレス | 48バイト | パケットを送信した側のMACアドレス       | 
| イーサタイプ      | 16バイト | 0800 IPプロトコル<br>0806 ARPプロトコル | 

MACアドレスは個々のLANアダプタのROMに紐付けられた世界に一つだけのアドレス

IPヘッダーを作るときに宛先IPアドレスはDNSルックアップでわかりました
ただ宛先MACアドレスはわかりません
そのときどうするべきか、
ここで使われるのがARPというプロトコルで、イーサネットがサブネット内のつながっている全員にパケットを届けるブロードキャストという仕組みで「XXXXというIPアドレスを持っている人はいませんか？いたらMACアドレスを教えて下さい」というと該当者から「私のMACアドレスはYYYYです」という応答が帰ってきます。
全員にパケットが届けられたのですが、該当しなかった人は返答を何も返さずにパケットを捨てます。
これがイーサネットの基本的な仕組みです。

# 第3章 ケーブルの先はLAN機器だった
#### ルーターの基本
ルーターの役目はIPアドレスを元に次のルーターに中継をすることです

パケットがハブから送られてきたとき、IPアドレスで中継先を判断する
このときにルーティングテーブルというサブネットを表すIPアドレスが載っているテーブルを見て、
送られてきたパケットの宛先IPアドレスを見て該当したらパケットをその宛先に中継する
ゲートウェイ欄に登録されているIPアドレスを持つルーターに対してインターフェース欄に登録されているポートからパケットを中継する

前提: 192.168.1.10というサーバー宛に送ったパケット
Aルーターから送られてきたパケットを受け取ったBルーターの動作
まずMACヘッダーは使用しないので捨ててIPヘッダーを見ます
ルーティングテーブルとパケットの宛先IPアドレスを比較します
192.168.1.0/24
192.168.1.0/25
上記のように同じアドレスがあった場合はネットワーク番号のビット数が長いもの(サブネットマスクが25の方)
を優先して選びます
理由としてネットワーク番号が長いということはホスト番号のビット数が少ないということで
割り当て可能な台数が少ないということで、範囲が絞り込まれているからです
逆に一致するものが一つも見つからなかった場合
ネットマスクが0のところにパケットを中継します
これはデフォルトゲートウェイと呼び、インターネットへの入口になります。
そして送信する前にTTLというIPヘッダーのフィールドを-1してから送信します
これによりパケットの中継ループを防ぎます(普通は起きないけど、ルーティングテーブルに中継先が正しく登録されていないときに起こる可能性がある)

# 第5章 サーバー側のLANには何がある
### ファイアウォールについて
サーバーの前に遮るものが何もない状態だと防御力がめちゃめちゃ弱いので対応する必要がある。
その防御のために使用されるのがファイアウォール
ファイアウォールは特定のサーバー上で動く特定のアプリケーションにアクセスするパケットだけを通し、それ以外のパケットを遮断する役割を持っている。そのおかげで外部からのアクセスを許可していないアプリケーションへのパケットの侵入を遮断してくれる。

### ファイアウォールの仕組み
アプリケーションにアクセスできるパケットを選り分けるために今は`パケットフィルタリング`という方法が最も普及している

### パケットフィルタリングとは
MACヘッダー、IPヘッダー、TCP or UDPヘッダーなどを見て適切なパケットかを判断すること

### IPヘッダーでのフィルタリング
#### インターネットから流れてくるパケットの場合
IPヘッダーの宛先IPアドレスと送信元IPアドレスを見て、始点と終点を判断する。インターネットから流れてくるパケットは始点を特定できないが、流れの終点はWebサーバーになるのでそこがあっているかを確認して、あっていない場合通さない

#### webサーバーからインターネットに流れるパケットの場合
始点がちゃんとwebサーバーのアドレスに一致するか確認し、あっていない場合は通さない

### TCPヘッダー、UDPヘッダーでのフィルタリング

#### インターネットから流れてくるパケットの場合
- 宛先ポート番号を調べてがwebサーバーと一致しているか
- TCPヘッダーのコントロールビットで判断

#### webサーバーからインターネットに流れるパケットの場合
- 送信元ポート番号を調べてがwebサーバーと一致しているか
- TCPヘッダーのコントロールビットで判断

#### 複数サーバーにリクエストを振り分けてサーバーの負荷の分散をする
サーバーへのアクセスが増えたときにサーバーの処理能力が追いつかなくなることがあります
このときにサーバーのスペックを単純に上げるスケールアップとサーバの台数を増やしてアクセスを振り分けるスケールアウトという方法があります
振り分ける機器はロードバランサーといいます
ロードバランサーのIPアドレスをDNSサーバーに登録することでクライアントのリクエストを直接Webサーバーにするのではなく、ロードバランサーに変えられ、ロードバランサーがそのリクエストをさばくことができます

> ラウンドロビン：リクエストをすべてのサーバーに均等に振り分ける方式です。各サーバーで処理するリクエストの数が均等になるため、平均的にはサーバの負荷も均等になります。﻿
リーストコネクション：その時点で一番接続数が少ないサーバーに送信する方式です。﻿
IPハッシュ：同じIPアドレスからのリクエストは、同じ振り分け先サーバーへ振り分けられる方式です。﻿
ノード単位の分散：クライアント（利用者）を1つの単位とし、同じクライアントからのすべてのリクエストを同じ分散対象サーバに振り分ける方式です。

#### キャッシュサーバーを利用したサーバーの負荷分散
ロードバランサーの他にも負荷分散の方法がありまして、そのうちの一つがキャッシュサーバーになります
キャッシュサーバーはプロキシという仕組みを使用して、webサーバーとクライアントの間に入り、webサーバーから受け取ったデータを保存しておき、そのデータをwebサーバーに代わってクライアントに送り返す機能を持ちます。

例えば計算量に膨大な時間がかかるけど、頻繁に返す値は変わらないようなリクエストがあるとします。
それはwebサーバー的にもクライアント的にもきついです
なのでキャッシュにやってもらいます
キャッシュの仕組みとしてはロードバランサーのように、DNSサーバーにwebサーバーのIPアドレスを登録する代わりにキャッシュサーバーのIPアドレスを登録します。
そしてキャッシュサーバーがクライアントのリクエストを受け取ります
キャッシュサーバーはリクエストを受取り、そのデータが自分のキャッシュに保存されているのか確認します
もし保存されていなければ、webサーバーに対してデータを貰い、そのデータをキャッシュに保存しつつ、クライアントに変えします
次にもし保存されていたら、キャッシュサーバーからwebサーバーに対して前回保存されたん日付を送り、webサーバー側はその日付を確認してもしその日付から変更があればデータを、変更がなければ変更はありませんという内容を送り返します。
こうすることで、webサーバーの負荷は下がることになります。

#### コンテンツ配信サービス
先程のキャッシュサーバーはwebサーバー側に置くものでしたが、webサーバーとクライアントの間(経路上)に置くキャッシュサーバがあり、それはCDN(Content Delivery Network)と呼ばれています。
CDNはいろいろな場所に存在するので、リクエストを送ったクライアントの近くにあることもあります。
そうすることでレスポンス速度に大きく寄与します。