技術向上

プログラミングの学び、気になるテクノロジーやビジネストレンドを発信

TCP scanner【Go】

bufio packageのScannerを使って1行ずつ読み込むことができます。
TCP接続した情報を読み込む例を示します。

main.goです。

func main() {
    li, err := net.Listen("tcp", ":8080")
    if err != nil {
        log.Fatal(err)
    }

    for {
        conn, err := li.Accept()
        if err != nil {
            log.Fatal(err)
        }
        go handle(conn)
    }
}

func handle(conn net.Conn) {
    scanner := bufio.NewScanner(conn)
    for scanner.Scan() {
        fmt.Println(scanner.Text())
    }
    if err := scanner.Err(); err != nil {
        log.Fatal(err)
    }
    defer conn.Close()
}


上記を実行し、ブラウザ上で:8080にアクセスすると、接続情報が出力されます。
別のターミナルからtelnetを使ってlocalhost 8080に接続すると、
そのターミナルに入力した値が、そのままファイル実行側のターミナルに出力されます。

また、forループの中で、Fprintを使用すると書き込みも行うことができます。

for scanner.Scan() {
    fmt.Println(scanner.Text())
    fmt.Fprintf(conn, "You said, %v", scanner.Text())
}


bufio.NewScannerでポインタを作成した後に、Splitメソッドを使うと、
行の区切り方を指定することもできます。

scanner := bufio.NewScanner(conn)
scanner.Split(bufio.ScanWords)    // 単語で区切る


bufio.Scannerは、ReadString()やReadBytes()よりも記述量が少なくなり、便利ではありますが、1行のデータ量が、上限を超えてしまうとscanを中断してしまう、という問題があります。デフォルトですと、その上限は65536バイトです。
あらかじめ1行のサイズ上限がわかっている場合には、scanner.Buffer()を使って最大のバッファサイズを指定します。

const (
    initialBufSize = 10000
    maxBufSize = 1000000
)

func handle(conn net.Conn) {
    scanner := bufio.NewScanner(conn)
    buf := make([]byte, initialBufSize)
    scanner.Buffer(buf, maxBufSize)
    ...
}

scanが始まった後にscanner.Buffer()を実行するとpanicが発生します。

あらかじめ1行の上限がわからない場合は、bufio.Reader.ReadBytes()などを使用するのがよいでしょう。

参考

mickey24.hatenablog.com