関連記事
GitHub - devlights/blog-summary: ブログ「いろいろ備忘録日記」のまとめ
概要
以下、自分用のメモです。
UNIXドメインソケットにはLinux版のみの機能として「抽象名前空間」(Abstract Namespace)という概念があります。
通常、UNIXドメインソケットを利用する場合は実体となるファイルが必要となりますが、抽象名前空間を用いる場合はファイルを作成する必要はありません。
以下の特徴を持ちます。
- ソケットのアドレス(名前)の先頭にNULバイト(\0)を付ける
- ファイルシステム上にソケットファイルを作成しない
- プロセス終了時に自動的にクリーンアップされる
知ってると結構便利な概念ですので、ついでなのでGoでサンプル作ってみました。syscallパッケージを使って低レイヤーな処理で扱うこともできますし、netパッケージからでも利用できます。
今回は net パッケージの Conn を利用した版。
サンプル
main.go
/* UNIXドメインソケット 抽象名前空間のサンプル(netパッケージのConnを利用する版) */ package main import ( "bytes" "errors" "flag" "fmt" "io" "log" "net" "os" "os/signal" "time" ) 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 { ln, err := net.Listen("unix", serverAddr) if err != nil { return err } defer ln.Close() sigCh := make(chan os.Signal, 1) signal.Notify(sigCh, os.Interrupt) go func(sigCh <-chan os.Signal) { <-sigCh log.Println("[S] Shutdown...") ln.Close() os.Exit(0) }(sigCh) buf := make([]byte, bufSize) for { conn, err := ln.Accept() if err != nil { if errors.Is(err, net.ErrClosed) { return nil } return err } // サンプルなので1接続で占有状態とする errCh := make(chan error) go func() { defer close(errCh) defer func() { conn.Close() log.Println("[S] close") }() clear(buf) n, err := conn.Read(buf) if n == 0 || errors.Is(err, io.EOF) { log.Println("[S] disconnect") return } if err != nil { errCh <- err } message := buf[:n] log.Printf("[S] Recv (%s)", message) message = bytes.ToUpper(buf[:n]) _, err = conn.Write(message) if err != nil { errCh <- err } log.Printf("[S] Send (%s)", message) // FIN待機用のタイムアウト設定 err = conn.SetReadDeadline(time.Now().Add(2 * time.Second)) if err != nil { log.Printf("[S] SetReadDeadline error: %v", err) errCh <- err return } for { clear(buf) if n, err = conn.Read(buf); n == 0 || errors.Is(err, io.EOF) { log.Println("[S] disconnect") break } if err != nil { if netErr, ok := err.(net.Error); ok && netErr.Timeout() { log.Println("[S] timeout (client FIN)") break } errCh <- err return } } }() err = <-errCh if err != nil { return err } } } func runClient() error { conn, err := net.Dial("unix", serverAddr) if err != nil { return err } defer func() { conn.Close() log.Println("[C] 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 n == 0 || errors.Is(err, io.EOF) { log.Println("[S] disconnect") return nil } if err != nil { return err } message := string(buf[:n]) log.Printf("[C] Recv (%s)", message) // Graceful shutdown { unixConn, ok := conn.(*net.UnixConn) if !ok { return fmt.Errorf("conn.(*net.UnixConn) failed") } err = unixConn.CloseWrite() if err != nil { return err } log.Println("[C] SEND FIN (shutdown(SHUT_WR))") for { clear(buf) if n, err = conn.Read(buf); n == 0 || errors.Is(err, io.EOF) { log.Println("[C] disconnect") break } } } 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 02:42:06.032066 [C] Send (hello) 02:42:06.032128 [S] Recv (hello) 02:42:06.032345 [S] Send (HELLO) 02:42:06.032365 [C] Recv (HELLO) 02:42:06.032378 [C] SEND FIN (shutdown(SHUT_WR)) 02:42:06.032382 [S] disconnect 02:42:06.032410 [S] close 02:42:06.032413 [C] disconnect 02:42:06.032436 [C] close task: [run] pkill -INT -f './app -server' 02:42:06.043557 [S] Shutdown...
参考情報
- https://man7.org/linux/man-pages/man7/unix.7.html
- https://siguniang.wordpress.com/2012/04/29/unix-domain-socket-address-types/
Goのおすすめ書籍
過去の記事については、以下のページからご参照下さい。
サンプルコードは、以下の場所で公開しています。





