ネットワークプログラミングの基本的なシステムコールを理解する

## はじめに
RailsやNext.jsなどのアプリケーションフレームワークを使えば、簡単にネットワークを介したアプリケーションを作成することができますが、その背後にはOSが提供する基本的なネットワーク操作が存在しています。

アプリケーションはユーザランドに属しているため、カーネルランドに属しているTCP/UDP等のトランスポート層の機能を使うためにはシステムコールを呼び出す必要があります。

今回は`TCP`でネットワークプログラミングをする際に必要なシステムコールについて調べていきます。

## socket()
まずは`socket()`を呼び出すことによって、アプリケーション層とトランスポート層の間に口を作ります。
```c
#include <sys/socket.h>

int socket(int domain, int type, int protocol);
```
呼び出しの際の引数はそれぞれ以下を意味します。

- domain
    - どのプロトコルファミリーで通信するかを指定します
    - ex
        - `AF_INET`: IPv4 Internet protocols
        - `AF_INET6`: IPv6 Internet protocols
        - `AF_BLUETOOTH`: Bluetooth low-level socket protocol
- type
    - ソケットのタイプを指定します
    - ex
        - `SOCK_STREAM`: TCPコネクションで使われるsocketタイプ
        - `SOCK_DGRAM`: UDP通信で使われるsocketタイプ
- protocol
    - ソケットが使用するプロトコルを指定します
    - ここで指定できるプロトコルは`/etc/protocols`で管理されています

戻り値は、この呼び出しによって作成されたsocketを参照するfile descriptorです。

https://man7.org/linux/man-pages/man2/socket.2.html

## bind()
`socket()`で作成された、socketに対してアドレスを割り当てます。
ここで言うアドレスとはIPアドレスとport番号のことです。
```c
#include <sys/socket.h>

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
```
- sockfd
    - bindするsocketのfile descirptorを指定します
    - `socket()`の戻り値がここに入ります
- addr
    - 割り当てるアドレスを指定します
- addrlen
    - 第2引数の`const struct sockaddr *addr`で表される構造体のサイズを指定します

Serverの場合は特定のportに対してbindすることがほとんどですが、Clientからリクエストを送る際にもこの`bind()`は呼び出されています。

Clientからリクエストを送る際にはbind先を指定しないことがほとんどなので、kernelが暗黙に指定していますが、これらのportのことをephemeral portと呼びます。
ephemeral portとして使うことができる範囲は決められていて、`/proc/sys/net/ipv4/ip_local_port_range`で確認することができます。

https://man7.org/linux/man-pages/man2/bind.2.html

## listen()
`listen()`によって作成されたsocketがClient用ではなくてServer用であることを明示します。
この時点でServerにおけるClientからの接続要求の準備が整います。

```c
#include <sys/socket.h>

int listen(int sockfd, int backlog);
```
- sockfd
    - 対象のsocketのfile descriptorを指定します
- backlog
    - queueに入れられる処理待ちのconnectionの最大サイズを指定します
    - このサイズに到達した場合、Clientはエラーを受け取ります

https://man7.org/linux/man-pages/man2/listen.2.html

## accept(), accept4()
`accept()`で、接続待ちコネクションが入っているqueueからコネクションを取り出して、そのコネクションに対応するためのread/write用のsocketを作成します。

https://doc.rust-lang.org/std/net/struct.TcpListener.html

```c
#include <sys/socket.h>

int accept(int sockfd, struct sockaddr *_Nullable restrict addr,
          socklen_t *_Nullable restrict addrlen);
```
- sockfd
    - listening用のsocketのファイルディスクリプタを指定します
    - socket()の戻り値に等しいです
- addr
    - 自socketのアドレスではなく、接続要求をしてきたClientのアドレスを指定します
- addrlen
    - 第2引数の構造体のサイズを指定します

戻り値は、コネクションに対応するために作成したread/write用のsocketのfile descriptorです。

https://man7.org/linux/man-pages/man2/accept.2.html

## recv()
`recv()`で、対象のsocketからmessageを受け取ります。

```c
#include <sys/socket.h>

ssize_t recv(int sockfd, void buf[.len], size_t len, int flags);
```
- sockfd
    - Server: `accpet()`で作成したsocketのfile descriptを指定します
    - Client: `socket()`で作成したsocketのfile descriptを指定します
- buf
    - messageを受け取るためのbufferのポインタを指定します
- len
    - 第2引数のbufferの最大サイズを指定します
- flags
    - 受信時の動作を指定するオプションフラグです

戻り値は受信したmessageのサイズになります。

https://man7.org/linux/man-pages/man2/recv.2.html

## send()
`send()`によって、接続しているsocketに対してmessageを送信します。

```c
#include <sys/socket.h>

ssize_t send(int sockfd, const void buf[.len], size_t len, int flags);
```

- sockfd
    - Server: `accpet()`で作成したsocketのfile descriptを指定します
    - Client: `socket()`で作成したsocketのfile descriptを指定します
- buf
    - 送信したいデータが乗っているbufferのポインタを指定します
- len
    - bufferのサイズを指定します
- flags
    - 送信時の動作を指定するオプションフラグです

https://man7.org/linux/man-pages/man2/sendmsg.2.html

## 参考

https://tatsu-zine.com/books/superuser-linux-system

https://gihyo.jp/book/2024/978-4-297-13893-6

https://github.com/momijiame/linux-tcpip-book

https://gihyo.jp/book/2024/978-4-297-14157-8

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