Skip to content

Add socket/unix_domain_socket/abstract-namespace/use-net-conn #905

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 1, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# UNIXドメインソケットの抽象名前空間のサンプル (net-conn版)

Linux特有の機能である「抽象名前空間」(Abstract Namespace)のUnixドメインソケットは以下の特徴がある

- ソケットのアドレス(名前)の先頭にNULバイト(\0)を付ける
- ファイルシステム上にソケットファイルを作成しない
- プロセス終了時に自動的にクリーンアップされる

netパッケージで利用する場合、アドレスの先頭に @ を付与しておくと、内部で NULバイト(\0) に置き換えてくれる。

## 実行例

```sh
$ task
task: [build] go build -o app main.go
task: [run] ./app -server &
task: [run] sleep 1
task: [run] ./app
01:59:18.896796 [C] Send (hello)
01:59:18.896882 [S] Recv (hello)
01:59:18.897098 [S] Send (HELLO)
01:59:18.897149 [C] Recv (HELLO)
01:59:18.897174 [C] close
01:59:18.897177 [S] disconnect
01:59:18.897205 [S] close
task: [run] pkill -INT -f './app -server'
01:59:18.909751 [S] Shutdown...
```

## 参考情報

- https://man7.org/linux/man-pages/man7/unix.7.html
- https://siguniang.wordpress.com/2012/04/29/unix-domain-socket-address-types/
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# 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 -INT -f './{{.APP_NAME}} -server'
clean:
cmds:
- rm -f ./{{.APP_NAME}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
/*
UNIXドメインソケット 抽象名前空間のサンプル(netパッケージのConnを利用する版)
*/
package main

import (
"bytes"
"errors"
"flag"
"fmt"
"io"
"log"
"net"
"os"
"os/signal"
)

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)

// Graceful shutdown
{
unixConn, ok := conn.(*net.UnixConn)
if !ok {
errCh <- fmt.Errorf("conn.(*net.UnixConn) failed")
return
}

err = unixConn.CloseWrite()
if err != nil {
errCh <- err
return
}

for {
clear(buf)
if n, err = conn.Read(buf); n == 0 || errors.Is(err, io.EOF) {
log.Println("[S] disconnect")
break
}
}
}
}()

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)

return nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
app
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# UNIXドメインソケットの抽象名前空間のサンプル (net版)
# UNIXドメインソケットの抽象名前空間のサンプル (net-unixconn版)

Linux特有の機能である「抽象名前空間」(Abstract Namespace)のUnixドメインソケットは以下の特徴がある

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
/*
UNIXドメインソケット 抽象名前空間のサンプル(netパッケージのUnixConnを利用する版)
*/
package main

import (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
/*
UNIXドメインソケット 抽象名前空間のサンプル(syscallパッケージを利用する版)
*/
package main

import (
Expand Down
Loading