なんか Plan9 とか見てみてから…とか思ってたら全然見ないみたいなので前書いたものを置いておくことに。
サーバ
C だとお決まりの socket bind listen accept で作る。以下同時接続数 1 の echo サーバ。
#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/un.h> int main() { int sock; struct sockaddr_un addr; if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) { perror("socket"); return 1; } memset(&addr, 0, sizeof(addr)); addr.sun_family = PF_UNIX; strcpy(addr.sun_path, "/tmp/hoge"); if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) { perror("bind"); return 1; } if (listen(sock, 1) < 0) { perror("listen"); return 1; } while (1) { struct sockaddr_un dummy; int fd; int len = sizeof(dummy); if ((fd = accept(sock, (struct sockaddr*)&dummy, &len)) < 0) { perror("accept"); return 1; } while (1) { int ret; char c; ret = read(fd, &c, 1); if (ret < 0) { perror("read"); return 1; } else if (ret == 0) { break; } write(fd, &c, 1); } close(fd); } }
はしょって書いたのに長いな…これでも同時接続できないし。 Ruby は UNIXServer なるクラスを使うとほとんど TCPServer と同じでラク。
require 'socket' USOCK = '/tmp/hoge' if File.exist? USOCK File.unlink USOCK end UNIXServer.open(USOCK) do |serv| while true s = serv.accept while true c = s.getc if c s.putc c else break end end end end
こんくらいだとちゃんとスレッド作って同時接続対応とかしてやってもいい気がしてきます。
でもまぁもっとラクな方法があって、 Ubuntu だと ucspi-unix ってパッケージに入ってる unixserver コマンドを使うと
unixserver /tmp/hoge cat
で終わり。ちゃんと終了時にソケット削除してくれるし良い。接続数設定とかもできるし。要は tcpserver とかみたいに標準入出力とやりとりするコマンドがなんでもサーバになってくれるわけ。なんか tcpserver が手元に入ってないと気付いたので tcpsvd とやらで代用すると、
tcpsvd 0 9999 cat
とかとおなじ。
クライアント
telnet -u が便利…だと思ったら MacOSX と FreeBSD には telnet -u あるんだけど、手元の Linux には無いみたいで困る。 unixclient では作れないかなぁ…と思ったので Ruby でサックリと。
require 'socket' sock = UNIXSocket.open(ARGV[0]) inputs = [STDIN, sock] while true i, o, e = IO.select(inputs) i.each do |input| if input == STDIN c = STDIN.getc if c sock.putc(c) sock.flush else sock.close_write inputs = [sock] end elsif input == sock c = sock.getc if c STDOUT.putc(c) else exit end end end end
ucspi-unix には unixcat ってのがついていて、それで良さげに思えるんだけど、中身見ると
exec unixclient "$1" sh -c 'exec cat <&6'
とかいう shell script (unixclient は unixserver のクライアントバージョンで、指定したプログラムと fd 6 と 7 を使ってやりとりする) で、これは unix domain socket の吐いた内容の出力はできるけど入力はできないと思う。入力をやるなら、
cat >&7 cat <&6
とかを実行させればいい気がするんだけど、なんにせよこれらだと接続終了時に終了しない。これはたぶんサーバ側の cat が終わってないからで、一個目の cat が終了した後に終わらない理由は書き込み側の終了を伝えてないからだと思う。 7 を閉じちゃえばいいようにも思えるんだけど、これ socket だから 6 も 7 も同じファイルディスクリプタで書き込み側とかの区別がなくて、閉じちゃえば接続が切れてしまって今度は受信ができない。 shell から shutdown(fd, SHUT_WR); 的なことができるといいんだけど。
このへんの事情は TCP socket も同じで、よくわからんけど、読み書きを一つのファイルディスクリプタでやるっていう設計のせいなのかなぁ。 TODO: Plan9 とかどうやってるんだろう。