Socket()とかBind()とかを理解する

## この記事の概要

サーバーサイドを勉強していると、`socket()`やら`bind()`というような難しそうな用語がよく登場します。今まではこれらの概念から目を背けてきましたが、一人前のエンジニアになるためには理解しておく必要がありそうです。

しかし、フロントエンドと違い、バックエンド関係の記事はとにかく小難しいです。古い記事が多いですし、難しい単語を羅列して攻撃してきます。

なので、落ちている記事を自分なりに噛み砕いて、ここに置いておこうと思います。

ここからは、[コネクション型通信：サーバプログラムの作成](http://research.nii.ac.jp/~ichiro/syspro98/server.html)という分かりやすい記事を主軸として、細かい部分を[Linuxのマニュアルページ](https://linuxjm.osdn.jp/index.html)で補完しながら書いていきます。



## プログラムの流れ

サーバのプログラムは、６つのフェーズから構成されます。

|                | はじめ               |                         |
| -------------- | -------------------- | ----------------------- |
|                | ↓                    |                         |
| socket()       | (1) ソケット生成     |                         |
|                | ↓                    |                         |
| bind()         | (2) ソケット登録     |                         |
|                | ↓                    |                         |
| listen()       | (3) ソケット接続準備 |                         |
|                | ↓                    |                         |
| accept()       | (4) ソケット接続待機 | ← 接続要求              |
|                | ↓                    |                         |
| recv() / send() | (5) 受信/送信        | ← データ → クライアント |
|                | ↓                    |                         |
| close()        | (6) ソケット切断     |                         |
|                | ↓                    |                         |
|                | 終わり               |                         |

以下、

* socket()
* bind()
* listen()
* accept()
* read() / write()
* close()

の6つのフェーズについて、それぞれ説明していきます。



> **注意**
>
> TCPとUDPでは、同じソケットの概念でも、手順等が大きく異なります。
>
> [このページ](http://www.ne.jp/asahi/hishidama/home/tech/socket/)に分かりやすく比較した表があったので、引用させていただきます。
>
> <img width="1136" alt="スクリーンショット 2019-06-27 15.33.16.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227791/56770b0e-ba8d-c77e-ea61-538047788df8.png">

>
> コメントで教えていただきました。ありがとうございます。



## (1)  Socket()

### 概要

[ソケット](#ソケット)を生成する。引数には[TCP](#tcp)通信に利用するプロトコルの種類を与える。プロトコルの種類とは、インターネット用であることや、TCPと[UDP](#udp)の種別も含まれる。



### 書式

```c
int socket(int domain, int type, int protocol);  
```

参考 : [Socket](https://linuxjm.osdn.jp/html/LDP_man-pages/man2/socket.2.html)



### 詳細

#### 引数

* **domain**（第１引数）
  どの[プロトコルファミリー](#プロトコルファミリー)を通信に使うかを指定する。
  例）
  * Unix
  * IP
  * IPv6

* **type**（第２引数）
  通信方式を指定する。
  例）
  * 順序性と信頼性があり、双方向に接続する。
  * 信頼性はあるが、順序は保証しない。
  
* **protocol** （第３引数）
  ソケットが使用するプロトコルを指定する。通常、プロトコルファミリーの種類ごとに一つのプロトコルのみをサポートする。その場合は0を指定する。
#### 返り値
* 各ソケットの識別子となる[ファイルディスクリプター](#ファイルディスクリプター)を返す。



## (2)  Bind()

### 概要

`socket()`を実行してもソケットが作られただけであり、[ポート](#ポート)番号などは未確定。なので、`bind()`を用いてポート番号などをソケットに割り当てる。



### 書式

```c
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
```

参考 : [Bind](https://linuxjm.osdn.jp/html/LDP_man-pages/man2/bind.2.html)



### 詳細

#### 引数

* **sockfd**（第１引数）
任意のソケットを示すファイルディスクリプター。

* **addr**（第２引数）
  ソケットに割り当てる[アドレス](#アドレス)。

* **addrlen**（第３引数）
  addrの指す[構造体](#構造体)のサイズ。

#### 返り値

成功した場合はゼロを返す。エラー時には-1を返す。



## (3)  Listen()

### 概要

ソケットを接続待ちの状態にする。



### 書式

```c
int listen(int sockfd, int backlog);
```

参考 : [Listen](https://linuxjm.osdn.jp/html/LDP_man-pages/man2/listen.2.html)



### 詳細

#### 引数

* **sockfd**（第１引数）
  任意のソケットを示すファイルディスクリプター。

* **backlog**（第２引数）
  保留中のキューの最大長。キューがいっぱいの状態で接続要求が到着すると、クライアントはエラーを受け取る。

#### 返り値

成功した場合はゼロを返す。エラー時には-1を返す。



## (4)  Accept()

### 概要

`accept()`を実行すると、クライアントから通信接続要求が来るまでプログラムを停止し、接続後にプログラムを再開する。



### 書式

```c
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
```

参考 : [Accept](https://linuxjm.osdn.jp/html/LDP_man-pages/man2/accept.2.html#lbAM)



### 詳細

#### 引数

* **sockfd**（第１引数）
  任意のソケットを示すファイルディスクリプター。

* **addr**（第２引数）
  接続した相手のIPアドレスやポート番号などの情報を含む。
  
* **addrlen**（第３引数）
  addrの指す構造体のサイズ。
  
  

#### 返り値

成功した場合は、受け付けたソケットのファイルディスクリプターを返す。

エラー時には-1を返す。



## (5)  Read() / Write()

> ここでは`read()`と`write()`としていますが、一般には`recv()`と`send()`が使われるそうです。
>
> また、`listen()`するための`socket()`と、データ読み書き用の`socket()`は別です。データ読み書き用の`socket()`は`accept`できた分だけ作成されるので、マルチプロセス/スレッドな構成にすることで、独立した`socket()`で多重通信ができるそうです。
>
> コメントで教えていただきました！　ありがとうございます。

### 概要

クライアントから送られてきたデータの受信`read()`と、送信`write()`を行う。

### 書式

```c
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);  
```

参考 : [Read](https://linuxjm.osdn.jp/html/LDP_man-pages/man2/read.2)、[Write](https://linuxjm.osdn.jp/html/LDP_man-pages/man2/write.2)



### 詳細

#### 引数

* **fd**（第１引数）
	読み込み/書き込み先を示すファイルディスクリプター
	
* **buf**（第２引数）
  読み込み/書き込み先を示すバッファ

* **count**（第３引数）
  読み込み/書き込みの最大バイト数

#### 返り値

読み込まれた/書き込まれたバイト数を返す。
エラー時には-1を返す。



## (6)  Close()

> TCPの場合、`close()`の代わりに**`shutdown()`**も使えるそうです。
>
> コメントより教えていただきました。ありがとうございます。

### 概要

ソケットを閉じて、通信接続を終了する。クローズされたファイルティスクリプターはどのファイルからも参照されていない状態になり、再利用できるようになる。



### 書式

```c
int close(int fd);
```

参考 : [Close](https://linuxjm.osdn.jp/html/LDP_man-pages/man2/close.2.html)



### 詳細

#### 引数
* **fd**（第１引数）  
  任意のソケットを示すファイルディスクリプター。

#### 返り値

成功した場合は0を返す。エラー時には-1を返す。



## その他・用語の解説

### 引数と返り値

プログラムの関数には引数を入れると返り値が帰ってきます。LinuxのシステムはC言語でできているので、この記事で紹介している書式も全てC言語です。

### ソケット

システム間の通信を可能にするもの。

### TCP

標準的に利用されている通信方法です。信頼性が高い通信をすることができます。

### UDP

TCPほど信頼性は高くありませんが、高速です。リアルタイム性を求められる通信に利用されます。

### プロトコルファミリー

通信プロトコル(方法)をまとめたもの。

### ファイルディスクリプター

ファイルなどを識別するためのもの。よくFDと略される。

### ポート

TCP/UDPの概念です。よく「サーバーに空いている穴」と表現されます。ポートごとに担当するアプリケーションが違います。

### アドレス

システム上での特定の場所を示す番地のことです。

### 構造体

いろんなデータを格納できる便利な箱です。



## まとめ

`socket()`とか`bind()`という用語は難しそうですが、サーバーのシステムの流れの一部として捉えると、概念自体はそこまで難しくないのではないかと思います。

おかしなところや誤字脱字にお気付きの方がおられましたら、お気軽に編集リクエスト / コメントしてください。