Quantcast
Channel: プログラミング
Viewing all articles
Browse latest Browse all 8315

Goメモ-461 (golang.org/x/sys/unixを使ってソケット通信)(01-基本パターン) - いろいろ備忘録日記

$
0
0

関連記事

GitHub - devlights/blog-summary: ブログ「いろいろ備忘録日記」のまとめ

概要

以下、自分用のメモです。忘れないうちにメモメモ。。。

最近、ひょんなことで、golang.org/x/sys/unixを使ってソケット通信する処理をちょっと書いたので、ついでにここにメモ残しておこうと思いました。

syscallパッケージを使っても同じことが出来るのですが、少し呼び出すAPIが異なるかもしれません。

今回は、基本パターンみたいなもの。C言語でソケット使ったことある人にはお馴染みな感じですね。

サンプル

サーバとクライアントを用意します。サンプルなので、1度のリクエスト・レスポンスで終わりです。

Server

//go:build linuxpackage 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
    }
    deferfunc() {
        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
    }
    deferfunc() {
        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
            }
        }
    }

    returnnil
}

Client

//go:build linuxpackage 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
    }
    deferfunc() {
        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])

    returnnil
}

Taskfile

実行用に以下のようなタスクファイルを用意

# https://taskfile.devversion:'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:trueclean: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]sleep1
task: [run] ./bin/client
17:05:29.140452[SERVER] Connect from [127001]: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]sleep1
task: [run] pgrep server && pkill server

参考情報

Goメモ-428 (flaggyメモ)(01) - いろいろ備忘録日記

Goメモ-429 (flaggyメモ)(02) - いろいろ備忘録日記

Goメモ-430 (flaggyメモ)(03) - いろいろ備忘録日記

Goのおすすめ書籍


過去の記事については、以下のページからご参照下さい。

サンプルコードは、以下の場所で公開しています。


Viewing all articles
Browse latest Browse all 8315

Trending Articles