以下の内容はhttps://devlights.hatenablog.com/entry/2025/04/16/073000より取得しました。


Goメモ-562 (net.ListenerとSO_REUSEADDR)

関連記事

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

概要

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

通信処理にて、リスナを起動する際、C言語とかでは SO_REUSEADDR をセットすることが多いです。

で、Goではソケットが抽象化されているため、実際にセットされているのかどうかをちょっと調べてみました。(ついでに SO_REUSEPORTも)

環境は Linux (Ubuntu Linux 22.04.5) です。

$ cat /etc/os-release
PRETTY_NAME="Ubuntu 22.04.5 LTS"
NAME="Ubuntu"
VERSION_ID="22.04"
VERSION="22.04.5 LTS (Jammy Jellyfish)"
VERSION_CODENAME=jammy
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=jammy

サンプル

main.go

package main

import (
    "context"
    "flag"
    "fmt"
    "log"
    "net"
    "syscall"
)

type (
    Args struct {
        UseListenConfig bool
    }
)

const (
    SO_REUSEADDR = syscall.SO_REUSEADDR
    SO_REUSEPORT = 0xf
)

var (
    args Args
)

func main() {
    log.SetFlags(log.Lmicroseconds)

    flag.BoolVar(&args.UseListenConfig, "listenconfig", false, "Use net.ListenConfig")
    flag.Parse()

    if err := run(); err != nil {
        panic(err)
    }
}

func run() error {
    var err error
    switch args.UseListenConfig {
    case true:
        err = runListenConfig()
    default:
        err = runNormal()
    }

    if err != nil {
        return err
    }

    return nil
}

func runListenConfig() error {
    lc := net.ListenConfig{
        Control: func(network, address string, c syscall.RawConn) error {
            var (
                reuseAddr, reusePort int
                opErr1, opErr2       error
                err                  error
            )

            err = c.Control(func(fd uintptr) {
                reuseAddr, opErr1 = syscall.GetsockoptInt(int(fd), syscall.SOL_SOCKET, SO_REUSEADDR)
                reusePort, opErr2 = syscall.GetsockoptInt(int(fd), syscall.SOL_SOCKET, SO_REUSEPORT)
            })

            if err != nil {
                return err
            }

            log.Printf("SO_REUSEADDR=%d", reuseAddr)
            log.Printf("SO_REUSEPORT=%d", reusePort)

            if opErr1 != nil {
                return opErr1
            }

            if opErr2 != nil {
                return opErr2
            }

            return nil
        },
    }

    ln, err := lc.Listen(context.Background(), "tcp", ":8888")
    if err != nil {
        return err
    }
    defer ln.Close()

    return nil
}

func runNormal() error {
    ln, err := net.Listen("tcp", ":8888")
    if err != nil {
        return err
    }
    defer ln.Close()

    tcpLn, ok := ln.(*net.TCPListener)
    if !ok {
        return fmt.Errorf("not a TCP Listener")
    }

    file, err := tcpLn.File()
    if err != nil {
        return err
    }
    defer file.Close()

    var (
        fd = file.Fd()
        v  int
    )
    v, err = syscall.GetsockoptInt(int(fd), syscall.SOL_SOCKET, SO_REUSEADDR)
    if err != nil {
        return err
    }
    log.Printf("SO_REUSEADDR=%d", v)

    v, err = syscall.GetsockoptInt(int(fd), syscall.SOL_SOCKET, SO_REUSEPORT)
    if err != nil {
        return err
    }
    log.Printf("SO_REUSEPORT=%d", v)

    return nil
}

Taskfile.yml

# https://taskfile.dev

version: '3'

tasks:
  default:
    cmds:
      - cat /etc/os-release | head -n 1
      - task: run
  run:
    cmds:
      - go run . -listenconfig
      - go run .

実行

$ task
task: [default] cat /etc/os-release | head -n 1
PRETTY_NAME="Ubuntu 22.04.5 LTS"
task: [run] go run . -listenconfig
09:02:50.949184 SO_REUSEADDR=1
09:02:50.949316 SO_REUSEPORT=0
task: [run] go run .
09:02:51.148105 SO_REUSEADDR=1
09:02:51.148219 SO_REUSEPORT=0

SO_REUSEADDR でデフォルトで設定されてて、SO_REUSEPORTについて設定されていないという結果でした。

なので、GoでLinuxでリスナを作って処理する場合、リスナ終了後にTIME_WAITとなっているポートを再利用出来る状態となっています。

参考情報

Goのおすすめ書籍


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

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




以上の内容はhttps://devlights.hatenablog.com/entry/2025/04/16/073000より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14