UNIXドメインソケットとは
UNIXドメインソケットは、単一のコンピュータ内で動作する
プロセス間通信(IPC)を実現するための機能です。BSDソケットを基盤とし、TCP/IPのようなネットワークプロトコルを使用せずに、ローカルマシン内での効率的なデータ交換を可能にします。
UNIXドメインソケットの仕組み
UNIXドメインソケットは、ファイルシステムをアドレス空間として利用します。プロセスは、ファイルシステム内のinodeを通じてソケットを参照し、同じソケットを共有することで通信を行います。この通信は、オペレーティングシステムのカーネル内で完結するため、ネットワークを介した通信と比較して高速かつ効率的です。
データ転送に加え、`sendmsg()`や`recvmsg()`といった
システムコールを利用することで、
ファイル記述子をやり取りすることもできます。これにより、プロセス間でリソースを共有する際の柔軟性が向上します。
UNIXドメインソケットの特徴
- - 高速な通信: 単一マシン内での通信に特化しているため、ネットワークプロトコルに伴うオーバーヘッドが少なく、高速なデータ転送が可能です。
- - 双方向通信: ソケットを通じて双方向のデータ送受信が可能です。
- - ファイル記述子の転送: `sendmsg()`および`recvmsg()`システムコールを用いて、プロセス間でファイル記述子を転送できます。
- - プロセスforkが不要: 通信を行うために、プロセスをforkする必要はありません。
- - POSIX準拠: BSDソケットの`AF_UNIX`プロトコルファミリーとして提供されており、`SOCK_STREAM`、`SOCK_DGRAM`、`SOCK_SEQPACKET`の3種類のソケットタイプに対応しています。
利用例
C言語
`socket()`関数で`AF_UNIX`を指定し、`SOCK_STREAM`または`SOCK_DGRAM`を選択します。クライアント側は`connect()`を呼び出して接続を確立します。
`junixsocket`や`JUDS`といったライブラリを利用します。
Java 16からは、`java.nio.channels.SocketChannel`でも利用可能です。
Android
`android.net.LocalSocket`クラスが提供されています。
Mono
`Mono.Unix.UnixClient`クラスで利用可能です。
PHP
URLを`unix://`または`udg://`で始めることで利用できます。
ソースコード例
サーバー (C99)
c
include
include
include
include
include
include
define SOCKET_PATH "/tmp/my_socket"
int main() {
int server_fd, client_fd;
struct sockaddr_un server_addr, client_addr;
socklen_t client_addr_len = sizeof(client_addr);
char buffer[1024];
// ソケットの作成
if ((server_fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
perror("socket");
exit(EXIT_FAILURE);
}
// アドレスの設定
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sun_family = AF_UNIX;
strncpy(server_addr.sun_path, SOCKET_PATH, sizeof(server_addr.sun_path) - 1);
unlink(SOCKET_PATH);
// ソケットへのバインド
if (bind(server_fd, (struct sockaddr )&server_addr, sizeof(server_addr)) == -1) {
perror("bind");
close(server_fd);
exit(EXIT_FAILURE);
}
// リッスン開始
if (listen(server_fd, 5) == -1) {
perror("listen");
close(server_fd);
exit(EXIT_FAILURE);
}
printf("サーバー起動。クライアントからの接続を待機中...
");
// クライアントからの接続を待機
if ((client_fd = accept(server_fd, (struct sockaddr )&client_addr, &client_addr_len)) == -1) {
perror("accept");
close(server_fd);
exit(EXIT_FAILURE);
}
// クライアントからのデータ受信
ssize_t bytes_received = recv(client_fd, buffer, sizeof(buffer) - 1, 0);
if (bytes_received == -1) {
perror("recv");
close(client_fd);
close(server_fd);
exit(EXIT_FAILURE);
} else if (bytes_received == 0) {
printf("クライアントが切断しました。
");
} else {
buffer[bytes_received] = '\0'; // Null terminate the received data
printf("クライアントからのメッセージ: %s
", buffer);
}
// クライアントにメッセージを送信
char message = "Hello from server!";
ssize_t bytes_sent = send(client_fd, message, strlen(message), 0);
if (bytes_sent == -1) {
perror("send");
} else {
printf("サーバーからのメッセージ送信完了。
");
}
// ソケットを閉じる
close(client_fd);
close(server_fd);
unlink(SOCKET_PATH);
return 0;
}
クライアント (C99)
c
include
include
include
include
include
include
define SOCKET_PATH "/tmp/my_socket"
int main() {
int client_fd;
struct sockaddr_un server_addr;
char buffer[1024];
// ソケットの作成
if ((client_fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
perror("socket");
exit(EXIT_FAILURE);
}
// アドレスの設定
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sun_family = AF_UNIX;
strncpy(server_addr.sun_path, SOCKET_PATH, sizeof(server_addr.sun_path) - 1);
// サーバーへの接続
if (connect(client_fd, (struct sockaddr )&server_addr, sizeof(server_addr)) == -1) {
perror("connect");
close(client_fd);
exit(EXIT_FAILURE);
}
printf("サーバーに接続しました。
");
// サーバーにメッセージを送信
char *message = "Hello from client!";
ssize_t bytes_sent = send(client_fd, message, strlen(message), 0);
if (bytes_sent == -1) {
perror("send");
} else {
printf("クライアントからのメッセージ送信完了。
");
}
// サーバーからのデータ受信
ssize_t bytes_received = recv(client_fd, buffer, sizeof(buffer) - 1, 0);
if (bytes_received == -1) {
perror("recv");
} else if (bytes_received == 0) {
printf("サーバーが切断しました。
");
} else {
buffer[bytes_received] = '\0';
printf("サーバーからのメッセージ: %s
", buffer);
}
// ソケットを閉じる
close(client_fd);
return 0;
}
実装
UNIXドメインソケットは、
LinuxカーネルではBSDソケットの`AF_UNIX`プロトコルファミリーとして実装されています。
システムコールを通じてAPIが提供されています。
内部的には、バッファへの読み書きというシンプルな仕組みで実現されています。`SOCK_STREAM`の場合、カーネル内部でソケットバッファへのメッセージコピー、受信側の`sk_data_ready`呼び出しを繰り返してデータ転送を行います。
TCPのようなプロトコルスタックを必要としないため、プロトコルによるデータの入れ子構造がなく、パケットロスや到達順序保証などの考慮も不要です。これにより、高速かつ効率的な
プロセス間通信が可能になります。
特殊なUNIXドメインソケット
`socketpair()`関数を利用すると、双方向通信が可能なUNIXドメインソケットを2つ作成できます。一方のソケットに書き込まれたデータは、もう一方のソケットから読み出すことができます。これは、一方向通信しかできないパイプと比較して、より柔軟な
プロセス間通信を実現します。
脚注
関連情報