ソケットの概要 (走り書き)

## この記事は？

ソケットについて調べた際の走り書きです。

## 調べた動機

- HTTP - (ソケット？) - TCP - IP - …
- MySQL の `/tmp/mysql.sock` もソケット？

<img alt='OSI 参照モデル' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6697/2ab70707-2271-a1f4-c68f-9921728f04d3.png' width="800">

[OSI参照モデル > 例 > 層別の例 - Wikipedia](https://ja.wikipedia.org/wiki/OSI%E5%8F%82%E7%85%A7%E3%83%A2%E3%83%87%E3%83%AB#%E5%B1%A4%E5%88%A5%E3%81%AE%E4%BE%8B) より

## 内容

- ソケット: ネットワーク通信に用いるファイルディスクリプタ (file descriptor)
- UNIX 系 OS (Linux など) では入出力はすべてバイト列として扱われる。その入出力ではバイト列のことをストリーム (stream) と呼ぶ
- ファイルディスクリプタ: ストリームを区別する識別子 (整数)
    - 0: 標準入力
    - 1: 標準出力
    - 2: 標準エラー出力
    - そのほかのストリームはオープンされるたびに小さい番号から順に使われクローズされると同じ番号が再利用される
- ファイルディスクリプタが対応しているストリームの接続先がローカルのファイルやネットワーク経由の接続というように種類が異なっていても、全く同じように入出力できる
  - つまりストリームを透過的に扱える
  - ソケットではファイルに対する操作と同様の操作でネットワーク通信を進められる

| プロトコルファミリー<br>ソケットタイプ | SOCK_STREAM |SOCK_DGRAM | 特定方法 | 通信 |
|--|--|--|--|--|
| AF_INET<br>AF_INET6 | TCP | UDP | IP アドレス<br>ポート番号 | ホスト間 |
| AF_UNIX | UNIX ドメインソケット | UNIX ドメインソケット|ファイル名 | プロセス間 |

- ソケットの種類
  - INET ドメインソケットと UNIX ドメインソケット
- 特定方法
    - TCP: 5 タプル (プロトコル、送信側 IP、送信側ポート、受信側 IP、受信側ポート)
    - UNIX ドメインソケット:
        - MySQL サーバーがローカルクライアントと通信するために使用する UNIX ソケットファイルのデフォルトの場所は /tmp/mysql.sock である
- プロトコルファミリー
    - AF_INET: IPv4 インターネットプロトコル
    - AF_INET6: IPv6 インターネットプロトコル
    - AF_UNIX: ローカル通信
- ソケットタイプ
    - SOCK_STREAM: 順序性と信頼性があり、双方向の接続されたバイトストリームを提供する
      - 帯域外データ転送メカニズムもサポートする
    - SOCK_DGRAM: データグラムをサポートする
        - データグラム (datagram): 配送成功・到達時間・到達順序がネットワークサービスによって保証されることがないパケット交換網における基本転送単位
- ソケットによる通信を行う際に使用するシステムコール
  - システムコール: アプリケーションと Linux カーネルとの間の基本的なインターフェース。  
  - socket(), bind(), listen(), accept(), read(), write(), close()
    - ソケットはファイルディスクリプタなので、通常のファイルに対する場合と同様に read(), write() が使える

<img alt='TCP Server and TCP Client' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6697/3c18aa40-905a-19bc-ca57-9b9f9a9e770d.png'>


[Chapter 4. Elementary TCP Sockets - Shichao's Notes](https://notes.shichao.io/unp/ch4/) より

Ruby でソケットを扱ってみる。

```rb
require 'socket'

# TCP Server
server = TCPServer.new(12_345)

loop do
  # TCPServer#accept
  # クライアントからの接続要求を受け付け、接続した TCPSocket のインスタンスを返す。
  socket = server.accept
  socket.print(Time.now.strftime('%Y/%m/%d (%a) %H:%M:%S'))
  socket.close
end
```

```rb
require 'socket'

# TCP Client
TCPSocket.open('localhost', 12_345).read
#=> "2024/11/16 (Sat) 21:30:31"

# サーバを閉じた後。
TCPSocket.open('localhost', 12_345).read
# Connection refused - connect(2) for "localhost" port 12345 (Errno::ECONNREFUSED)
```

```console
$ lsof -i:12345
COMMAND   PID         USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
ruby    20178       quanon    8u  IPv4 0x9db00af21468648e      0t0  TCP localhost:61265->localhost:italk (CLOSE_WAIT)
ruby    20482       quanon   10u  IPv6  0x2c83fda6bc3c44f      0t0  TCP *:italk (LISTEN)
```

```rb
require 'socket'

# TCP Server
server = UNIXServer.new('/tmp/sock')

loop do
  socket = server.accept
  socket.print(Time.now.strftime('%Y/%m/%d (%a) %H:%M:%S'))
  socket.close
end
```

```rb
require 'socket'

# TCP Client
UNIXSocket.open('/tmp/sock').read
#=> "2024/11/16 (Sat) 21:31:40"

# サーバを閉じた後。
UNIXSocket.open('/tmp/sock').read
# Connection refused - connect(2) for /tmp/sock (Errno::ECONNREFUSED)
```

## 参考

### 記事

- [まつもと直伝　プログラミングのオキテ 第16回　ネットワーク・プログラミング（ソケット編）](https://xtech.nikkei.com/it/article/COLUMN/20071031/285990/)
- [ファイル記述子 - Wikipedia](https://ja.wikipedia.org/wiki/%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E8%A8%98%E8%BF%B0%E5%AD%90)
- [ソケット (BSD) - Wikipedia](https://ja.wikipedia.org/wiki/%E3%82%BD%E3%82%B1%E3%83%83%E3%83%88_%28BSD%29)
- [macos - Get list of open files (descriptors) in OS X - Stack Overflow](https://stackoverflow.com/questions/20974438/get-list-of-open-files-descriptors-in-os-x)
- [調べなきゃ寝れない！と調べたら余計に寝れなくなったソケットの話 - Qiita](https://qiita.com/kuni-nakaji/items/d11219e4ad7c74ece748)
- [Man page of SOCKET](https://linuxjm.osdn.jp/html/LDP_man-pages/man2/socket.2.html)
- [Chapter 4. Elementary TCP Sockets - Shichao's Notes](https://notes.shichao.io/unp/ch4/)
- [RubyでいろいろなSocket作って遊んでみた。 - Qiita](https://qiita.com/MikuriyaHiroshi/items/b0a40f5e7b7be1ef327c)
- [Jep-380: unix domain socket channels – Inside.java](https://inside.java/2021/02/03/jep380-unix-domain-sockets-channels/)
  - 翻訳: [JEP-380: Unix domain socket channels – Logico Inside](https://logico-jp.io/2021/02/05/jep-380-unix-domain-socket-channels/)
- [fork 後に子プロセスにパイプを渡す](https://zenn.dev/moneyforward/articles/132403c22c44fb)


### 書籍

- [Amazon.co.jp: ポートとソケットがわかればインターネットがわかる――TCP/IP・ネットワーク技術を学びたいあなたのために (Software Design plus)](https://www.amazon.co.jp/-/ja/%E5%B0%8F%E5%B7%9D-%E6%99%83%E9%80%9A/dp/4774185701/)