習作で Unix Domain Socket を書いてみたのでメモを残しておく。
Python
TCP との違いは socket
初期化時に socket.AF_UNIX
を指定することと、 .bind()
の引数がパスになるくらいです。ただ、指定されたファイルがすでにあったらエラーになるので、 .bind()
する直前に unlink()
しておきます。
uds_server.py
# !/usr/bin/env python
import os
import signal
import socket
signal.signal(signal.SIGPIPE, signal.SIG_IGN)
sock = socket.socket(socket.AF_UNIX)
os.unlink('test.uds')
sock.bind('test.uds')
sock.listen(10)
while True:
s, addr = sock.accept()
print "connected from:", addr
while True:
s.sendall("a"*60 + '\n')
C
Python に比べると sockaddr_un
を扱うのがちょっと面倒。 sockaddr_un.sun_path
の長さが短いことに注意。ディレクトリ構造が深いところにソケット作れないってことらしい。 Python でも結局は内部でこれを使っているので、パス長の制限からは逃げられないはず。
uds_server.c
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <errno.h>
# include <unistd.h>
# include <fcntl.h>
# include <sys/socket.h>
# include <sys/types.h>
# include <sys/un.h>
# include <netinet/in.h>
# include <signal.h>
static char buf[] = "aaaaaaaaaaaaaaaaaa\n";
int main()
{
int r;
int listen_fd = 0;
struct sockaddr_un local, remote;
signal(SIGPIPE, SIG_IGN);
listen_fd = socket(PF_UNIX, SOCK_STREAM, 0);
local.sun_family = AF_UNIX;
strcpy(local.sun_path, "test.uds");
unlink(local.sun_path);
r = bind(listen_fd, (struct sockaddr *)&local, sizeof(local));
if (r)
perror("failed to bind");
listen(listen_fd, 100);
for (;;) {
socklen_t len = sizeof(remote);
int remote_fd = accept(listen_fd, (struct sockaddr *)&remote, &len);
if (remote_fd < 0) {
perror("failed to accept");
return 0;
}
for (;;) {
int sent = write(remote_fd, buf, 20);
printf("sent %d bytes\n", sent);
if (sent < 0) {
printf("%d %s\n", errno, strerror(errno));
perror("fail to send");
close(remote_fd);
break;
}
}
}
}