関連記事
GitHub - devlights/blog-summary: ブログ「いろいろ備忘録日記」のまとめ
概要
以下、自分用のメモです。忘れないうちにメモメモ。。。
最近、ひょんなことで、golang.org/x/sys/unix を使ってソケット通信する処理をちょっと書いたので、ついでにここにメモ残しておこうと思いました。
syscall パッケージを使っても同じことが出来るのですが、少し呼び出すAPIが異なるかもしれません。
今回は、基本パターンみたいなもの。C言語でソケット使ったことある人にはお馴染みな感じですね。
サンプル
サーバとクライアントを用意します。サンプルなので、1度のリクエスト・レスポンスで終わりです。
Server
//go:build linux package main import ( "errors" "log" "net" "golang.org/x/sys/unix" ) func init() { log.SetFlags(log.Lmicroseconds) } func main() { if err := run(); err != nil { log.Fatal(err) } } func run() error { // // Create // var ( sfd int err error ) sfd, err = unix.Socket(unix.AF_INET, unix.SOCK_STREAM, unix.IPPROTO_TCP) if err != nil { return err } defer func() { log.Println("[SERVER] サーバーソケットクローズ") unix.Close(sfd) }() // // Bind and Listen // var ( ip = net.ParseIP("127.0.0.1") ipv4 [4]byte sAddr unix.Sockaddr backLog = 2 ) copy(ipv4[:], ip.To4()) sAddr = &unix.SockaddrInet4{Port: 8888, Addr: ipv4} err = unix.Bind(sfd, sAddr) if err != nil { return err } err = unix.Listen(sfd, backLog) if err != nil { return err } // // Accept // var ( cfd int cAddr unix.Sockaddr ) cfd, cAddr, err = unix.Accept(sfd) if err != nil { return err } defer func() { log.Println("[SERVER] パケット送受信用ソケットクローズ") unix.Close(cfd) }() cAddrInet4 := cAddr.(*unix.SockaddrInet4) log.Printf("[SERVER] Connect from %v:%v", cAddrInet4.Addr, cAddrInet4.Port) // // Recv // var ( buf = make([]byte, 2048) n int ) n, err = unix.Read(cfd, buf) if err != nil { return err } log.Printf("[SERVER] %s", string(buf[:n])) // // Send // var ( msg = "HELLOWORLD" ) clear(buf) copy(buf, []byte(msg)) err = unix.Send(cfd, buf[:len(msg)], 0) if err != nil { return err } // // Disconnect detection // LOOP: for { clear(buf) n, err = unix.Read(cfd, buf) switch { case n == 0: log.Println("[SERVER] 切断検知 (0 byte read)") break LOOP case err != nil: var sysErr unix.Errno if errors.As(err, &sysErr); sysErr == unix.ECONNRESET { log.Printf("[SERVER] 切断検知 (%s)", sysErr) break LOOP } } } return nil }
Client
//go:build linux package main import ( "log" "net" "golang.org/x/sys/unix" ) func init() { log.SetFlags(log.Lmicroseconds) } func main() { if err := run(); err != nil { log.Fatal(err) } } func run() error { var ( sfd int err error ) sfd, err = unix.Socket(unix.AF_INET, unix.SOCK_STREAM, unix.IPPROTO_TCP) if err != nil { return err } defer func() { log.Println("[CLIENT] ソケットクローズ") unix.Close(sfd) }() var ( ip = net.ParseIP("127.0.0.1") ipv4 [4]byte sAddr unix.Sockaddr ) copy(ipv4[:], ip.To4()) sAddr = &unix.SockaddrInet4{Port: 8888, Addr: ipv4} err = unix.Connect(sfd, sAddr) if err != nil { return err } // // Send // var ( buf = make([]byte, 2048) msg = "helloworld" ) copy(buf, []byte(msg)) err = unix.Send(sfd, buf[:len(msg)], 0) if err != nil { return err } // // Recv // var ( n int ) clear(buf) n, err = unix.Read(sfd, buf) if err != nil { return err } log.Printf("[CLIENT] %s", buf[:n]) return nil }
Taskfile
実行用に以下のようなタスクファイルを用意
# https://taskfile.dev version: '3' tasks: default: cmds: - task: run fmt: cmds: - goimports -w . prepare: cmds: - mkdir -p bin build: deps: [ fmt ] cmds: - go build -o bin/server server/server.go - go build -o bin/client client/client.go run: deps: [ build ] cmds: - ./bin/server & - sleep 1 - ./bin/client - sleep 1 - pgrep server && pkill server ignore_error: true clean: cmds: - rm -rf ./bin
実行
task: [fmt] goimports -w . task: [build] go build -o bin/server server/server.go task: [build] go build -o bin/client client/client.go task: [run] ./bin/server & task: [run] sleep 1 task: [run] ./bin/client 17:05:29.140452 [SERVER] Connect from [127 0 0 1]:56374 17:05:29.140712 [SERVER] helloworld 17:05:29.140754 [CLIENT] HELLOWORLD 17:05:29.140915 [CLIENT] ソケットクローズ 17:05:29.140988 [SERVER] 切断検知 (0 byte read) 17:05:29.141024 [SERVER] パケット送受信用ソケットクローズ 17:05:29.141089 [SERVER] サーバーソケットクローズ task: [run] sleep 1 task: [run] pgrep server && pkill server
参考情報
Goメモ-428 (flaggyメモ)(01) - いろいろ備忘録日記
Goメモ-429 (flaggyメモ)(02) - いろいろ備忘録日記
Goメモ-430 (flaggyメモ)(03) - いろいろ備忘録日記
Goのおすすめ書籍
過去の記事については、以下のページからご参照下さい。
サンプルコードは、以下の場所で公開しています。