UNIXドメインソケット

**UNIXドメインソケット**（）は単一マシン上の高効率なプロセス間通信に用いられる機能・インタフェースの一種である。

プロセス間通信 (Inter-Process Communication: IPC) は一般的に名前付きパイプやBSDソケットを利用したTCP通信などで実現できる。UNIXドメインソケットはBSDソケットの一種であり、単一マシン上でのプロセス間通信を目的としている。ソケット通信がもつ双方性・プロセスfork不要といった特徴を備えつつ、単一マシン上の通信である（=インターネットを介さない）ことを生かした高効率な通信を可能にしている。

UNIXドメインソケットは、アドレス・名前空間としてファイルシステムを使用している。具体的には、ソケットをファイルシステム内のinodeとして作成し、そのパス名をソケットのアドレスとする。これにより、2つのプロセスが通信するために、同じソケットを開くことができる。しかし、コミュニケーションは、完全にオペレーティングシステムのカーネル内で発生する。データを送ることに加えて、プロセスは、`sendmsg()`および`recvmsg()`システムコールを使用してUNIXドメインソケット接続を経由してファイル記述子を送信することができる。 

POSIXはBSDソケットとしてUNIXドメインソケットインタフェースを提供している。BSDソケットの`AF_UNIX`protocol familyがUNIXドメインソケットに相当する。受け入れ可能なsocket typeは`SOCK_STREAM`・`SOCK_DGRAM`・`SOCK_SEQPACKET`の3種類である。

Go言語（の実行環境）では、Microsoft Windows上でもUNIXドメインソケットを使ったプログラムを動かすことができる。

特殊なUNIXドメインソケットとして、`socketpair()`を用いて作られるものがある。`socketpair()`はUNIXドメインソケットを2点作成し、一方のソケットに書き込んだデータがもう一方のソケットから読み出せる。パイプが一方通行の読み書きしかできないのに対し、`socketpair()`にて作成したソケットでは双方向通信が可能である。

## 利用方法
### C言語
`socket()` 関数における第1引数の domain に `AF_UNIX` を指定する。第2引数の type は `SOCK_STREAM`, `SOCK_DGRAM` どちらも利用可能。そして、クライアント側の場合、その後 `connect()` を呼ぶ。

### Java
以下のライブラリなどで利用可能。
* junixsocket
* JUDS
また、JEP380によりJava 16より`java.nio.channels.SocketChannel`で利用可能

### Android
Android SDKに、Java/Kotlinから利用できる `android.net.LocalSocket` クラスが実装されている。

### Mono
`Mono.Unix.UnixClient` クラスで利用可能。

### PHP
URL を unix:// もしくは udg:// で始めることで利用可能。

## ソースコード例
### サーバ
C99 でのサーバ側のソースコード例は以下の通り。


```c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>

int main(void)
{
    // サーバーソケット作成
    int sock = socket(AF_UNIX, SOCK_STREAM, 0);
    if (sock == -1)
    {
        perror("socket");
        return 1;
    }

    // struct sockaddr_un 作成
    struct sockaddr_un sa = {0};
    sa.sun_family = AF_UNIX;
    strcpy(sa.sun_path, "/tmp/unix-domain-socket");

    // 既に同一ファイルが存在していたら削除
    remove(sa.sun_path);

    // バインド
    if (bind(sock, (struct sockaddr*) &sa, sizeof(struct sockaddr_un)) == -1)
    {
        perror("bind");
        goto bail;
    }

    // リッスン
    if (listen(sock, 128) == -1)
    {
        perror("listen");
        goto bail;
    }

    while (1)
    {
        // クライアントの接続を待つ
        int fd = accept(sock, NULL, NULL);
        if (fd == -1)
        {
            perror("accept");
            goto bail;
        }

        // 受信
        char buffer[4096];
        int recv_size = read(fd, buffer, sizeof(buffer) - 1);
        if (recv_size == -1)
        {
            perror("read");
            close(fd);
            goto bail;
        }

        // 受信内容を表示
        buffer[recv_size] = '\0';
        printf("message: %s\n", buffer);

        // ソケットのクローズ
        if (close(fd) == -1)
        {
            perror("close");
            goto bail;
        }
    }

bail:
    // エラーが発生した場合の処理
    close(sock);
    return 1;
}
```


### クライアント
C99 でのクライアント側のソースコード例は以下の通り。


```c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>

#define MESSAGE "Hello World!"

int main(void)
{
    // ソケット作成
    int sock = socket(AF_UNIX, SOCK_STREAM, 0);
    if (sock == -1)
    {
        perror("socket");
        return 1;
    }

    // struct sockaddr_un 作成
    struct sockaddr_un sa = {0};
    sa.sun_family = AF_UNIX;
    strcpy(sa.sun_path, "/tmp/unix-domain-socket");

    // 接続
    if (connect(sock, (struct sockaddr*) &sa, sizeof(struct sockaddr_un)) == -1)
    {
        perror("connect");
        goto bail;
    }

    // 送信
    if (write(sock, MESSAGE, strlen(MESSAGE)) == -1)
    {
        perror("write");
        goto bail;
    }

    // クローズ
    close(sock);
    return 0;
 
bail:
    // エラーが発生した場合の処理
    close(sock);
    return 1;
}
```


## 実装

### Linux
LinuxカーネルにBSDソケット `AF_UNIX`protocol familyとして実装されている。SOCKET(2)をはじめとするシステムコールをAPIとして公開している。

#### 内部実装
LinuxにおけるUNIXドメインソケットは、バッファ（メモリ）への書き込みと読み込みという非常にシンプルな仕組みで実装されている。

Linuxカーネルの`linux/net/unix/af_unix.c` (GitHub mirror) に実装される。`SOCK_STREAM`の場合、カーネル内部ではソケットバッファへのメッセージコピー・peerがもつ`sk_receive_queue`のtail、受信側の`sk_data_ready`呼び出し、を繰り返すことでデータを転送する。

UNIXドメインソケットは単一マシン上のIPCが前提である。ゆえにTCPのようなプロトコルスイートは不要であり、プロトコルの重層が生むデータの入れ子構造を持たない。またネットワークに由来するパケットロスや到達順序保証の対応も必要ないため、バッファのread/writeというシンプルな仕組みで実装されている。結果として高効率なIPCが可能となっている。

## 制限事項
UNIXドメインソケットのアドレスとしてはそのパス名を用いるが、その最大長は概ね100バイト強に限られ、かつUNIXドメインソケットの実装に依存する。 これは、パス名一般の最大長よりも短い。この制限は、BSDによる初期の実装においてアドレスを格納する構造体`struct sockaddr_un`をmbufを用いて確保しており、そのメモリサイズが128バイトに固定されていたことに起因している。その後、実装変更により他のOSへの移植を含め、ネットワークスタック内部での制限は解消されたが、互換性のためにAPI上のアドレス最大長制限は残されている。

## 脚注

## 関連項目
* ソケット (BSD)

## 外部リンク
*仕様
**UNIX(7) Linux Programmer's Manual
**
**
**
* 
* ucspi-unix, UNIXドメインソケット クライアントサーバー コマンドラインツール
*解説
**Beej's Guide to Unix IPC - Unix Sockets
** Robert Watson (2005) "UNIXドメインソケット vs インターネットソケット"