関連記事
GitHub - devlights/blog-summary: ブログ「いろいろ備忘録日記」のまとめ
概要
以下、自分用のメモです。
UNIXドメインソケットにはLinux版のみの機能として「抽象名前空間」(Abstract Namespace)という概念があります。
通常、UNIXドメインソケットを利用する場合は実体となるファイルが必要となりますが、抽象名前空間を用いる場合はファイルを作成する必要はありません。
以下の特徴を持ちます。
- ソケットのアドレス(名前)の先頭にNULバイト(\0)を付ける
- ファイルシステム上にソケットファイルを作成しない
- プロセス終了時に自動的にクリーンアップされる
知ってると結構便利な概念ですので、ついでなのでGoでサンプル作ってみました。syscallパッケージを使って低レイヤーな処理で扱うこともできますし、netパッケージからでも利用できます。
今回は net パッケージの UnixConn を利用した版。
サンプル
main.go
package main import ( "bytes" "errors" "flag" "io" "log" "net" "os" "os/signal" "syscall" ) type ( Args struct { IsServer bool } ) const ( // 抽象名前空間のソケットアドレス(@記号で始まる名前は\0に変換される) serverAddr = "@go_unix_domain_socket_test" bufSize = len("hello") ) var ( args Args ) func init() { flag.BoolVar(&args.IsServer, "server", false, "server mode") } func main() { log.SetFlags(log.Lmicroseconds) flag.Parse() if err := run(); err != nil { panic(err) } } func run() error { var err error switch args.IsServer { case true: err = runServer() default: err = runClient() } if err != nil { return err } return nil } func runServer() error { addr, err := net.ResolveUnixAddr("unix", serverAddr) if err != nil { return err } ln, err := net.ListenUnix("unix", addr) if err != nil { return err } defer ln.Close() sigCh := make(chan os.Signal, 1) signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM) go func(sigCh <-chan os.Signal) { <-sigCh ln.Close() os.Exit(0) }(sigCh) conn, err := ln.AcceptUnix() if err != nil { return err } defer conn.Close() buf := make([]byte, bufSize) for { clear(buf) n, err := conn.Read(buf) if n == 0 || errors.Is(err, io.EOF) { log.Println("[S] disconnect") break } if err != nil { if errors.Is(err, syscall.EINTR) { continue } return err } message := buf[:n] log.Printf("[S] Recv (%s)", message) message = bytes.ToUpper(buf[:n]) _, err = conn.Write(message) if err != nil { return err } log.Printf("[S] Send (%s)", message) } return nil } func runClient() error { addr, err := net.ResolveUnixAddr("unix", serverAddr) if err != nil { return err } conn, err := net.DialUnix("unix", nil, addr) if err != nil { return err } defer conn.Close() buf := make([]byte, bufSize) copy(buf, []byte("hello")) _, err = conn.Write(buf) if err != nil { return err } log.Printf("[C] Send (%s)", buf) clear(buf) n, err := conn.Read(buf) if err != nil { if errors.Is(err, syscall.EINTR) { return nil } return err } if n == 0 { log.Println("[C] disconnect") return nil } message := string(buf[:n]) log.Printf("[C] Recv (%s)", message) return nil }
Taskfile.yml
# https://taskfile.dev version: '3' vars: APP_NAME: app tasks: default: cmds: - task: build - task: run build: cmds: - go build -o {{.APP_NAME}} main.go run: cmds: - ./{{.APP_NAME}} -server & - sleep 1 - ./{{.APP_NAME}} - pkill -f './{{.APP_NAME}} -server' || true clean: cmds: - rm -f ./{{.APP_NAME}}
実行
$ task task: [build] go build -o app main.go task: [run] ./app -server & task: [run] sleep 1 task: [run] ./app 09:01:13.238544 [C] Send (hello) 09:01:13.239925 [S] Recv (hello) 09:01:13.240143 [S] Send (HELLO) 09:01:13.240205 [C] Recv (HELLO) 09:01:13.240314 [S] disconnect task: [run] pkill -f './app -server' || true
参考情報
- https://man7.org/linux/man-pages/man7/unix.7.html
- https://siguniang.wordpress.com/2012/04/29/unix-domain-socket-address-types/
Goのおすすめ書籍
過去の記事については、以下のページからご参照下さい。
サンプルコードは、以下の場所で公開しています。