LoginSignup
17
15

More than 5 years have passed since last update.

golangで書くunix domain socketを使ったserver/clientのサンプルコード

Last updated at Posted at 2016-10-22

golangでunix domain socketを使って server/client 通信させるサンプルコードがあまりなくて、あってもサーバーからのレスポンスをハンドリングしてなかったりというのしか見つけられなかったので、サンプルでこのしておきます。

サーバー

この間書いた記事の簡易版です。
受信した文字列を2回繰り返したものを返却します。
また、受信・送信時に標準エラー出力にその内容を出力します。

server.go
package main

import (
    "io"
    "log"
    "net"
    "os"
)

func main() {
    log.SetFlags(log.Lshortfile)
    file := "/tmp/uds-sample-server.sock"
    // 削除はdeferで予約しておく
    // シグナルハンドラ定義しないと終了時に呼ばれない?
    defer os.Remove(file)
    listener, err := net.Listen("unix", file)
    if err != nil {
        log.Printf("error: %v\n", err)
        return
    }
    for {
        conn, err := listener.Accept()
        if err != nil {
            break
        }
        go func() {
            // Closeはdeferで予約しておく
            defer conn.Close()
            data := make([]byte, 0)
            for {
                // サンプルコードなのでバッファサイズを小さめに
                buf := make([]byte, 10)
                // 全データ受信後EOFがこない限りここで止まる
                nr, err := conn.Read(buf)
                if err != nil {
                    if err != io.EOF {
                        log.Printf("error: %v", err)
                    }
                    break
                }
                // 実際に読み込んだバイト以降のデータを除去したデータに変換
                buf = buf[:nr]
                log.Printf("receive: %s\n", string(buf))
                // slice同士の結合は二つ目のsliceの後ろに...をつける
                data = append(data, buf...)
                // レスポンスデータ送信
            }
            conn.Write(data)
            conn.Write(data)
            log.Printf("send: %s\n", string(append(data, data...)))
        }()
    }
}

クライアント

指定した文字列を送信して、返却された文字列を出力します。
また、送信・受信時に標準エラー出力にその内容を出力します。

client.go
package main

import (
    "fmt"
    "log"
    "net"
    "os"
)

func main() {
    log.SetFlags(log.Lshortfile)
    if len(os.Args) != 3 {
        return
    }
    file := os.Args[1]
    message := os.Args[2]
    conn, err := net.Dial("unix", file)
    if err != nil {
        log.Printf("error: %v\n", err)
        return
    }
    defer conn.Close()
    _, err = conn.Write([]byte(message))
    if err != nil {
        log.Printf("error: %v\n", err)
        return
    }
    log.Printf("send: %s\n", message)
    // これをしないとEOFが通知されずにレスポンスの処理まで進んでくれない
    err = conn.(*net.UnixConn).CloseWrite()
    if err != nil {
        log.Printf("error: %v\n", err)
        return
    }
    data := make([]byte, 0)
    for {
        // サンプルコードなのでバッファサイズを小さめに
        buf := make([]byte, 10)
        nr, err := conn.Read(buf)
        if err != nil {
            break
        }
        buf = buf[:nr]
        // 受信データのログ出力
        log.Printf("receive: %s\n", string(buf))
        data = append(data, buf...)
    }
    // 受信データの出力
    fmt.Printf("%s\n", string(data))
}

出力

server
$ go run server.go
# 以下はクライアントからデータを受信しての出力
server.go:43: receive: 0123456789
server.go:43: receive: abcdefghij
server.go:43: receive: klmnopqrst
server.go:43: receive: uvwxyz
# 以下はクライアントがWriteClose()してからの出力
server.go:50: send: 0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz
client
$ go run client.go /tmp/uds-sample-server.sock 0123456789abcdefghijklmnopqrstuvwxyz
# サーバーへの送信データ
client.go:28: send: 0123456789abcdefghijklmnopqrstuvwxyz
# サーバーからの受信データ
client.go:44: receive: 0123456789
client.go:44: receive: abcdefghij
client.go:44: receive: klmnopqrst
client.go:44: receive: uvwxyz0123
client.go:44: receive: 456789abcd
client.go:44: receive: efghijklmn
client.go:44: receive: opqrstuvwx
client.go:44: receive: yz
# 標準出力への表示
0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz
17
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
15