ネットワーク基礎の基礎 ~TCP/IPからHTTPまでをすっきり理解する

皆さんはネットワークは得意でしょうか？
Webが全盛期の今、SE（またはプログラマ）であっても、ネットワークの基礎をちゃんと理解しているかどうかで結構差がつくものです。
この記事では、ネットワークの基礎の基礎から、L7としてHTTPまでを例に解説していきたいと思います。

#ネットワークのことはじめ
ネットワークと広義に言ってしまうと様々なものを含みますが、ここでは皆さんが普段使っている有線LAN/無線LAN、TCP/IPを基本としてお話しします。
ところで、このTCPとかIPとかの事をProtocol（プロトコル）といいます。いうなれば通信するための事細かな決まり事ですね。

TCP/IP普及以前はベンダーごとにプロトコルが異なっていたため、ベンダーをまたいだ通信はできなかったそうです。
イメージ的にはDELLのPCとHPのPCは通信できなかった…みたいな感じです。今ですと信じられないですね。
確かWindows95あたりからTCP/IPが爆発的に普及し、基本的にどのPC間でも通信ができるようになりました。（この辺りは私も世代ではないのでよく知らないですが）

さて、そんなネットワークを勉強しようとすると、「OSI基本参照モデル」という物が最初に出てきます。第1層（L1／Layer1）の物理層から、第7層（L7）の応用層（またはアプリケーション層）まで、それぞれの役割が事細かに分けられています。これが制定されたのは、1982年頃らしく、だいーぶ昔です。
何が言いたいのかというと、OSI基本参照モデルは古すぎてTCP/IP世代の分類にそぐわないんです。なお、TCP/IPの分類のためには、TCP/IPのプロトコルスタックという物が提唱されていますが、OSI基本参照モデル程有名ではありません。
OSI基本参照モデルとTCP/IPのプロトコルスタックは、以下の図のような関係になります。
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478224/9b7200c4-5fe0-214c-ceac-8f3d7cdc3ef9.png)
なお、何故今の時代にそぐわないのにOSI基本参照モデルの話をしたかというと、L2、L3、L4、L7という表現が今でも頻繁に使われているからです。
現場で使われる時、L2はL2以下、要するにデータリンク層と物理層を特に分けずに、「ネットワークインターフェイス層」としてとらえます。
同様に、L4より上はL7として総称されます。

#各プロトコルの関連
WebブラウザからURLを入力してサーバへ伝わるとき、実際にサーバに飛ばされるデータは、PC内で各プロトコルと協調して送りだされます。
具体的には、WebブラウザがHTTPのパケットを作り、それがTCPパケットに押し込められ、それがさらにIPに押し込められ、最後にMACフレームに押し込まれて送信されます。
具体的には以下のようなイメージになります。
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478224/7f45a57d-6646-d2e8-ed6f-577022169bf7.png)
ちなみに、例えば一つのTCPパケットは必ず一つのIPパケットに入るかというとそんなことはなく、分割されたりすることもあります。
また、パケットとかフレームとかいう言葉が混在してますが、これはプロトコル毎に名前が違います。
ややこしいので間違えてたらごめんなさい。

基本的にインターネットはTCP/IPの世界なので、MACフレームは途中の経路で別のプロトコルに変換されてるかもですが、最終的に相手側のサーバには、IPパケットが届きます。受け取ったサーバでは、パケットを順に脱がしていって、最終的にHTTPデータを得るわけです。

#L2（TCP/IPのプロトコルスタックで言うネットワークインターフェイス層）
ここからは各プロトコルの詳細について話していきたいと思います。
まずL2。通常はEthernetが使用されます。ちなみにEthernetとMAC（Media Access Control）の違いがよくわかってないんで、ちょっとごっちゃになるかもです。
有線LANでも無線LANでも、MACフレームを使って、隣接するノードと通信します。
ここで言うノードとは、PCとか、ネットワーク機器とか、サーバなどの総称です。
MACアドレスという48bitのアドレスで識別します。
MACアドレスは、Ethernetに対応した機器を購入すると既に割り当てられていて、仮想MACアドレスを除けば自身で割り当てる事はありません。

L2で覚えておいてほしい機器には、ブリッジ、ハブ（リピータハブ／スイッチングハブ／L2スイッチ）があります。
昔はハブといえばリピータハブでした。（かなり昔です。）
リピータハブは「馬鹿ハブ」とも言われ、受信したMACフレームをすべてのポートから送出しました。なので、搬送波の衝突が良く起きたようです。
衝突が検知されたら、ジャム信号という物を送り、それを受け取ったノードはランダムな期間を待って再送することで、うまく通信できるようにしていたそうです。
これをCSMA/CD（Carrier Sense Multiple Access/Collision Detection)と言います。
でも、これってノード数が多くなると、衝突の嵐になりますよね。
そこで登場するのが、ブリッジです。ブリッジは、あて先MACアドレスを調べて、それが所属するポートにのみデータを送出します。これにより、ネットワークをセグメント化（分離）し、余計なデータを送らずに済むので、衝突を起きにくくすることができます。
ただ、スイッチングハブの登場で、ブリッジはなりを潜めました。（多分。正直私はスイッチングハブ世代なので、ブリッジをよく知りません）

スイッチングハブは別名L2スイッチとも呼ばれます。
内部に、ポートごとに「この先にいるノードのMACアドレスは…」というテーブルを持っており、これを使用することで必要なポートにだけデータを送信することができます。なので、衝突も起きにくくなります。このMACテーブルは自動で学習されます。
今、
[PC A] --- [スイッチングハブ] --- [PC B]
このような構成でPC AからBにデータを送信することを考えます。
初めての通信の時はMACテーブルにはPC Bに該当するデータはないので、全てのポートからデータを送出します。
同時に、PC Aからのデータを受け取ったポートは、MACテーブルにPC AのMACアドレスを書き込みます。
その後、PC Bから返送があると、どのポートの先にPC Bがいるかわかります。これもまたMACテーブルに書きこむことで、次回から特定のポートにだけデータを送ることができます。
ただ、ここで一つ問題があります。PC Bを、スイッチングハブから一旦外して別のポートに付け替えるとどうなるでしょうか？MACテーブルのデータと現実があっていないので、通信できなくなります。そのため、MACテーブル一定期間で削除されるようになっています。また、削除される前でも、PC Bからデータが送信されれば、ポートが変わったことがわかるので、MACテーブルを書き換えて正常に通信できるようになります。

ここで余談なんですが、皆さんはWireSharkというソフトをご存知でしょうか？
これはネットワーク上のパケットを傍受するためのソフトウェアなのですが、通常MACでは、受信側では自分宛のMACフレームかどうか調べ、違ったら破棄、自分宛なら受け取る、という動作をします。ただ、Ethernetには「無差別モード」という物があり、それを使うと自分宛でないMACフレームも受け取ります。これにより、通信を解析できるわけですが、間にスイッチングハブがあると、基本的に自分宛のパケットとブロードキャストフレームしか届かないので、傍受は難しい、という事になります。
（そのために、スイッチングハブによっては、全てのMACフレームを送出する特殊なポートを備えるものもあります）

あ、そういえばブロードキャストの説明をしていませんでした。
ブロードキャストは、特定の相手ではなく、「とにかく全員」に送るための方法です。
具体的にはあて先MACアドレスがオール０とか確かそんなパケットを送出します。
受け取り側は、自身のMACアドレスと異なっていても、ブロードキャストフレームであれば受け取ります。

もう一つ余談。
無線LANではCSMA/CDは使えないので、「衝突を回避する」方法として「CSMA/CA（Carrier Sense Multiple Access/Collision Avoidance）という物が使われます。
これは特に試験を受けるわけでなければ覚えなくていいです。

#L3 （TCP/IPのプロトコルスタックで言うインターネット層）
次はL3、IP（Internet Protocol)の出番です。
L2が隣接するノード同士を結ぶものだったのに対し、IPはあて先ノードまで届けるためのプロトコルです。
なお、現在IPはIPv4とIPv6がありますが、基本という意味でここではIPv4について述べます。以下、IPといったらIPv4を指します。

IPは「IPアドレス」を使用して接続先ノードを識別しますが、IPアドレスには大きく「プライベートアドレス」と「グローバルアドレス」があります。
グローバルアドレスはインターネットで使用されるアドレスで、インターネットに接続するためには必ずグローバルアドレスが必要になります。
プライベートアドレスは、LAN（Local Area Network）内、つまり会社内であったり、家庭内でのみ使用されるアドレスです。
プライベートアドレスではインターネットに出ていくことはできないので、インターネットとLANを結ぶルータ内でNAT（Network Address Transfer）という機能を使用することでプライベートアドレスをグローバルアドレスに変換して通信を行います。
なお、NATにも種類があり、広義のNATの説明は上記の通りですが、狭義にはNAT、NATP（またはIPマスカレード）があります。
狭義のNATは単純にプライベートアドレスとグローバルアドレスを紐づけるため、グローバルアドレスが一つしかない場合は同時にLAN内からは一つのノードしか通信できません。NAPT（Network Address and Port Transfer)は、プライベートアドレスと上位プロトコルのポート番号をセットにすることで、同時に複数ノードがインターネットに出れるようになります。現在、通常NATといったらNAPTを指します。（試験なんかだと違うかもです）

## IPアドレスの話
さて。IPアドレスはMACアドレスとは違い、利用する側が自分で指定するか、DHCP(Direct Host Configuration Protocol）を使用してIPアドレスを払い出してもらう必要があります。また、IPには「ネットワーク」という概念があり、異なるネットワークとはルータまたはL3スイッチを介さないと通信できないようになっています。
IPアドレスは、32bitの数値なのですが、ネットワーク部とホスト部に分ける事ができます。
IPの設定でネットマスク（255.255.225.0みたいない値）とか、192.168.1.1/24みたいな表記を見たことがないでしょうか？
実はこれが、ネットワーク部とホスト部を分けるカギとなります。
/24というのは、「上位24ビットがネットワークアドレスだよ！」という意味です。ネットマスクは、これと論理積を取った値がネットワークアドレスになります。
あ、言い忘れてましたけど、ホスト部がオール0のアドレスは「ネットワークアドレス」という特殊なアドレスになります。ホスト部がオール1のアドレスはブロードキャスト用のアドレスです。
先ほどの192.168.1.1/24なら、ネットワークアドレスが192.168.1.0、ホストアドレスが1、という事になります。
IPが限られてる状況だと、/28とか細かく指定したりするんですが、これをサブネットマスクで表すと255.255.255.240とか直感でわかりにくくなるので、早く滅びろ、と思っています。なお、/28みたいな書き方はCIDR（Classless Inter-Domain Routing／サイダー）といいます。
昔はIPはクラスで分けられていて（/8がクラスA、/16がクラスB、/24がクラスCに相当）、でもそれだとネットワークわけようとしたらIPが沢山必要になるよね、という事で生まれたものです。

ちなみに、ネットワークを細かく分けたい、という要求がなければこんな話はいらないはずなんです。
じゃぁネットワークを分けたいってどういう事？というと、通信させちゃいけいないノード同士を通信させないため、というのもあるんですが、「ブロードキャストドメイン」を小さくする、というのもあります。ブロードキャストは、MACの時と同様、全てのノードに対して送信するものですが、ブロードキャストドメインはそれが届く範囲です。大企業で誰かのブロードキャストが数十万人いる社員全員に届く…とかなったら嫌ですよね。（数百人でも嫌です）こういった理由も、ネットワークを分ける一つの動機です。

## DHCP(Direct Host Configuration Protocol）
多くの企業や家庭でDHCPは使われていますので、これを省略するわけにはいきますまい。
DHCPはIPアドレスを動的に付与するためのプロトコルで、DHCPを使うように設定されたノードでは、まず最初に「IPアドレスくれよ！」というパケットをブロードキャストするわけです。それを受け取ったDHCPサーバが、「んじゃお前これ使えよ」ってIPを払い出してくれるわけですね。
余談ですが、DHCPサーバが同一ネットワークにない場合どうなるでしょうか？別ネットワークという事は間にルータまたはL3スイッチがいるわけで、ブロードキャストはそれを越える事ができません。こういうケースでは、「DHCPリレー」という機能を持ったルータを使用します。要は、DHCP要求のブロードキャストだけは特別に通してあげますよ、という機能ですね。

## で、どうやって宛先ノードまで行くの？
はい。IPで通信を行うという事は、ブロードキャストを除き、相手の宛先IPアドレスがわかっている必要があります。
あ、こう書くとDNSの話が始まるかも…と思うかもですが、まだしません。
あて先のIPアドレスがわかっていたとしても、下位層のMACで通信できないと意味ないんですよね。
なので、IPでの通信に先立ち、ARP（Address Resolution Protocol）という物を使用して、あて先MACアドレスを得るわけです。
ARPでは、「このIPの人、MACアドレス教えてよ！」という通知をブロードキャストします。該当のPCがあれば、「あ！それおれだ！MACアドレスはXX:XX:XX:XX:XX:XXだよ！」と返信してくれます。ここで、あて先が同一ネットワークに居ない場合、ルータやL3スイッチが、代わりに自分のMACアドレスを教えてくれます。

そうやって解決したMACアドレスをあて先MACアドレスに設定し、あて先IPアドレスは既にわかっているものをセットしてパケットを送出するわけです。
（ちなみに、通常PC等は、内部でARPテーブルというキャッシュを持っていて、一度取得したIPに対応するMACアドレスは内部に保存して使いまわすようになっています。）
そのようなパケットを受け取ったら、ルータはあて先IPアドレスを見て次にパケットを飛ばす先のMACアドレスを決定し、あて先IPアドレスは変えずにあて先MACアドレスだけを書き換えてパケットを送信するわけです。これがリレーのようにつながれて、最終的にあて先ノードに到達できるわけです。

## ルータ、L3スイッチ
L3で知っておいてほしい機器は、NIC（Network Interface Card、これL2かも)、ルータ、L3スイッチです。
NICは要は有線LANのポートと考えてもらって差し支えないです。無線LANの場合でもNICとか言っちゃうかも知れません。これは用語としてそのようなものとして覚えておいてください。
ルータは、ルーティングテーブルに基づいて通信経路を決定する機器です。NE（Network Engineer）にでもなろうとしないなら、異なるネットワーク同士を結ぶ、程度の理解でいいと思います。L3スイッチは、ルータに似た機器で、L3ベースでスイッチング（宛先ポートにパケットを振り分ける）を行う機器です。

## その他
他、ルーティングテーブルやVLANについても（VLANはL2か）解説したいんですが、それは後に回すことにします。

#L4（TCP/IPのプロトコルスタックで言うトランスポート層）
続いてL4です。IPがあて先ノードを特定するのに対し、L4ではあて先ノード上の「特定のプログラム」を特定します。
IPだけ送っても、それがあて先のサーバ（またはPC）上で動いている、例えばApacheなのか、MySQLなのか、どのプログラムと通信したいのかわからないと、受け取り側も困りますよね。L4のTCPでは、ポート番号を使用して目的のブログラムを識別します。
ポート番号は、0～65535の値で、1023までは「ウェルノウンポート（well known port）」と呼ばれ、HTTPなら80、FTPなら20と21と言ったように決められています。
（決められていますが必ずしも従う必要はなく、特に攻撃者に知られたくない！なんて場合、敢えてHTTPだけど9080を使う、という事もあります）

ところで、IPというのは非常に無責任なプロトコルなんです。「俺送ったから。お前が受け取ったかどうかなんて知らないよ」そういうやつなんです。
TCP（Transmission Control Protocol）は、その無責任なIPの面倒も見てくれます。
どういうことかというと、目的のパケットがちゃんと届いたか確認して、届いてなければ「もう一回送ってよ！」とお願いしたり、様々な理由で分割されたパケットが順番通りに届かなかったとしても、ちゃんと順番通りに並べ替えて元の形に復元してくれる、とても優しい子なんです。

他、L4で良く使われるプロトコルにUDP（User Data Protocol）なんていうものがあります。
TCPは賢いんですが、「ブロードキャストができない」「パケットが全部届くまで待つ」という特徴があります。
特に、「パケットが全部届くまで待つ」というのは、動画配信や通話なんかではネックになります。
多少通信が乱れてもリアルタイム性が重要！というケースでは、UDPが使用されます。

#L7
ようやくここまでたどり着けました。
いや、長かった。
TCP/IPのプロトコルスタックでは、OSI基本参照モデルのL5以上は同一視されます。仕事の現場でも、L7といったらL5以上を指すことが殆どと感じます。
今回はWebエンジニアにイメージを持ってもらうことが目的なので、ここではHTTPについて話をさせて頂きます。

HTTPはHyper Text Transfer Protocolの略ですね。
ブラウザにURLを入力すると、まずDNS（Domain Name System）という機能を使用して、URLからIPアドレスを解決します。
DNSの詳細な説明はここでは省略します。ブラウザにURL形式ではなく、IP形式で入力すると、DNSの件は省略されます。
なお、DNSと言いましたが、厳密には（ほとんどのOSでは）まずhostsファイルを確認して、その中になければDNSキャッシュを見て、そこにもなければDNSに問い合わせに行きます。

その後、以下のような内容のHTTPパケット（パケットという言葉が正しいのかは知らない）を作って、サーバに投げます。
![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478224/9b66b07b-9991-8d4e-8b92-4f8820bfc621.png)

具体的な内容は、以下のようになります。
10年以上前に作った資料から引っ張り出してきたものなので諸々古いですが…
例えば、URLが
http://xxxxx.com/tashizan.add?valueA=10&valueB=25
だった場合、URLのホスト部（xxxxx.com）以降が、GETの後に続きます。

```
GET /tashizan.add?valueA=10&valueB=25 HTTP/1.1
Accept: image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*
Referer: http://localhost:8081/tashizan.add?valueA=10&valueB=25
Accept-Language: ja
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; YTB730; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET4.0C; .NET4.0E)
Accept-Encoding: gzip, deflate
Host: 127.0.0.1:8081
Connection: Keep-Alive
Cookie: btop_p=0&0&||||22985&0; __utma=1.739694334.1321928532.1321928532.1321928532.1; __utmz=1.1321928532.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)

```

HTTPのメソッドがPOSTの場合なんかは、以下のようになります。

```
POST /tashizan.add HTTP/1.1
Accept: image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*
Referer: http://localhost:8081/tashizan.add?valueA=10&valueB=25
Accept-Language: ja
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; YTB730; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET4.0C; .NET4.0E)
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate
Host: 127.0.0.1:8081
Content-Length: 19
Connection: Keep-Alive
Cache-Control: no-cache
Cookie: btop_p=0&0&||||22985&0; __utma=1.739694334.1321928532.1321928532.1321928532.1; __utmz=1.1321928532.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)

valueA=10&valueB=25
```

GETではパラメータがURLの後ろに?で区切られて続くのに対し、POSTはHTTPメッセージのボディ部に書かれます。
Webアプリを自前で作る場合、フレームワークを使用したり、PHPのようなWebに強い言語を使用したりしますが、クライアントから受け取ったパラメータがどのようにプログラムから受け取れるかは、フレームワークや言語により異なります。
PHPなら\$_GETや$_POSTで受け取ると思いますが、HTTPの内容が異なるため、こういった違いが生まれたりします。
言語やフレームワークによっては、その違いを吸収して同一の方法でとれるものなんかもあります。

上記のようなリクエストを受け取ったサーバは、レスポンスを返却します。
![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478224/45fc2e4d-fb2b-fb4e-fc96-ae28c7802934.png)
※ちょっと具体的な例が手元にないので、省略します。


#まとめ
ここまでの流れを大まかにまとめてみようと思います。
※細かいツッコミはなしでお願いします。

①PCを立ち上げると、OSが起動してネットワーク接続を開始する。
　この時、DHCPを使用する設定になっていれば、DHCP要求をブロードキャストし、DHCPサーバからIPアドレスを払い出してもらい設定する。
②ブラウザに「http://xxxxxx.com/」というURLをうち込む
③hostファイルを調べ、xxxxxx.comが書かれているか確認する書かれていなければDNSキャッシュを見る。なければDNSサーバに問い合わせてIPアドレスを知る。
④ARPテーブルにあて先IPに対応するMACアドレスがないか確認し、なければARP要求をブロードキャストする。すると、該当のIPまたはルータなどが、自身のMACアドレスを返却する。
⑤HTTPパケットをTCPパケットにつめ（httpでポート指定はないのでウェルノウンポートの80が設定される）、それをIPパケットに詰める。あて先アドレスは③で取得した物。送信元IPアドレスは自身のIPアドレス。さらにそれをMACフレーム（宛先は④で取得したもの）に詰めて送信する
⑥各種スイッチやルータなどを経由して、あて先サーバに届く。
⑦サーバはパケットの中身を順にはがしていき、ポート80で稼働しているプログラム（ここではApacheとする）にデータを渡す
⑧ApacheはHTTPパケットの内容に沿ってレスポンスを作成し、送信元アドレス宛に返却を行う。あて先ポートは、受信したTCPパケット内の送信元ポートを設定する
⑨各種スイッチやルータを等を経由して、PCに戻ってくる
⑩PCはパケットの中身を順にはがし、ブラウザにレスポンスを返却する
⑪ブラウザはレスポンスの内容をもとに、画面に表示する

という感じになります。
ちょっと長くなっちゃいましたけど、上記はSEには最低限知っておいてほしい内容です。

実はもうちょっと書きたいことがあって、ルーティングテーブルの話やサーバのクラスタリング、VLANなんかも触れておきたいと思っているので、いつか書こうと思います。
小さな間違いは見逃してほしいですが、大きな間違いがあれば突っ込んで頂けますと幸いです。