いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

Goメモ-285 (BackLogを指定した状態のnet.Listenerを作る)

概要

以下、自分用のメモです。

Go の net.Listen は、バックログが指定できないんですよね。。

githubの方でもイシューとしてOpen状態となっています。

github.com

ちなみに内部でバックログとして利用される値は linux の場合は net.core.somaxconn (/proc/sys/net/core/somaxconn) の値で

存在しない場合は、 syscallパッケージのSOMAXCONN (0x80) の定数値が利用される模様。

まあ、大丈夫といえば大丈夫なのですが、やっぱり指定したいですね。

dotnetとかだと普通に引数で指定できるので。(System.Net.Sockets.TcpListener::Start(Int32))

上記の理由により、どうしてもバックログの値を設定したい場合は syscall パッケージを利用して、ソケットを直接いじります。

C言語でソケットを作るのと同じ感じですね。こんな感じ。

// listen は、指定された情報で net.Listener を生成して返します.
//
// Go の net.Listen は、バックログが指定できないため
// テスト目的でバックログを極端に少ない状態にすることが出来ない。
// 利用される値は linux の場合は net.core.somaxconn の値であり
// 存在しない場合は、 syscall.SOMAXCONN の定数値が利用される。
//
// なので、バックログを指定したい場合はソケットを直接生成して
// net.FileListener に紐づけて、リスナーとして利用する。
//
// # REFERENCES
//   - https://github.com/golang/go/issues/39000
//   - https://github.com/golang/go/issues/41470
//   - https://github.com/valyala/tcplisten/blob/master/tcplisten.go
//   - https://stackoverflow.com/a/49593356
//   - https://meetup-jp.toast.com/1509
func listen(addr string, backLog int) (net.Listener, error) {
    // make tcp addr
    var (
        tcpAddr *net.TCPAddr
        err     error
    )

    tcpAddr, err = net.ResolveTCPAddr("tcp4", addr)
    if err != nil {
        return nil, err
    }

    // make socket addr
    var (
        sockAddr syscall.SockaddrInet4
    )

    sockAddr.Port = tcpAddr.Port
    copy(sockAddr.Addr[:], tcpAddr.IP.To4())

    // make socket file descriptor
    var (
        sockFd     int
        sockDomain = syscall.AF_INET
        sockType   = syscall.SOCK_STREAM | syscall.SOCK_NONBLOCK | syscall.SOCK_CLOEXEC
        sockProto  = syscall.IPPROTO_TCP
    )

    sockFd, err = syscall.Socket(sockDomain, sockType, sockProto)
    if err != nil {
        return nil, err
    }

    // bind
    err = syscall.Bind(sockFd, &sockAddr)
    if err != nil {
        return nil, err
    }

    // listen
    err = syscall.Listen(sockFd, backLog)
    if err != nil {
        return nil, err
    }

    // make net.Listener
    var (
        fname    = fmt.Sprintf("backlog.%d.%s.%s", os.Getpid(), "tcp4", addr)
        file     = os.NewFile(uintptr(sockFd), fname)
        listener net.Listener
    )
    defer file.Close()

    listener, err = net.FileListener(file)
    if err != nil {
        return nil, err
    }

    return listener, nil
}

参考情報

github.com

github.com

github.com

stackoverflow.com

meetup-jp.toast.com

github.com

Go言語による並行処理

Go言語による並行処理

Amazon


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

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