TCPのテスト用に空いているポートを探す

perl でいうと、Test::TCP::empty_port() が欲しかったのでちょっと書く。

import (
    "net";
    "fmt";
)
func empty_port() int {
    for port := 10000; port < 20000; port++ {
        addr   := fmt.Sprintf("localhost:%d", port);
        l, err := net.Listen("tcp", addr);
        if (err == nil) {
            l.Close();
            return port;
        }
    }
    panic("can't listen empty port");
}

空いてそうなポートを順番に Listen してみて、成功したら Close() してから返す。
ここで l は net.Listener 型の値なんだけど、ローカル変数にいれておいてもスコープから外れたときに自動で Close してくれたりはしないようだ。

関数を抜けるときに自動で実行される defer を使うとこんな風にも書ける。

func empty_port() int {
    for port := 10000; port < 20000; port++ {
        addr   := fmt.Sprintf("localhost:%d", port);
        l, err := net.Listen("tcp", addr);
        if (err == nil) {
            defer l.Close();
            return port;
        }
    }
    panic("can't listen empty port");
}

なんかあまり違わないようだけど、今回のようにすぐに return するのではなく、いろいろ処理してからあちこちで return するようなコードの場合は defer で閉じ忘れないようにしておくといいのかな。

[追記]
最初 net.Listen() の直後に

defer l.Close();

を書いていたら、Listen できなかった場合にも Close() を実行しようとして segmentation violation を起こしてしまっていた。

ちなみに defer は複数書けて、後に書いた方から実行される。

import . "fmt";
func main() {
    defer Println("first defer");
    Println("main 1");
    defer func() {
        Println("second defer 1");
        Println("second defer 2");
    }();
    Println("main 2");
}

実行結果

main 1
main 2
second defer 1
second defer 2
first defer