こんにちは、もーすけです。
「Goならわかるシステムプログラミング」を読み進めています。とてもいい本です。
読み進めながら実験したことなどを書いていきます。同じ本を読んでいる人の参考(寄り道?)になればと思います。
今回は「TCPソケットとHTTPの実装」です。
Goならわかるシステムプログラミングの学習シリーズ
- 第2章 io.Writer
- 第3章 io.Reader
- 第4章 チャネル
- 第5章 システムコール
- 第6章 TCPソケットとHTTPの実装
- Coming soon
「6.4 ソケット通信の基本構造」
「6.4 ソケット通信の基本構造」では、TCPサーバの実装の解説がありますが、さらっと流されているのでせっかくなので手元で動かして理解を深めてから、「6.5 Go言語でのHTTPサーバを実装する」に移りたいと思います。
Minimum TCPサーバ
本当に最小限のTCPサーバの実装。
TCPリクエストがくると処理はしますが、プログラムが終了してしまうため一度しか応答できません。
書籍ではTCPのクライアントもGoで書いていますが、もっと手軽に nc
コマンドでさくっと試してみましょう。
package main
import (
"io"
"net"
)
func main() {
ln, err := net.Listen("tcp", ":8080")
if err != nil {
panic(err)
}
conn, err := ln.Accept()
if err != nil {
panic(err)
}
// 2章 io.Writerで学んだことを復習
io.WriteString(conn, "hello from my tcp server!")
}
nc
コマンドでリクエスト送信後、リクエストは返ってきますが、2度目の送信ではレスポンスがなく、exitコードも1(エラー)であることが確認できました。
$ nc localhost 8080
hello from my tcp server!
$ nc localhost 8080
$ echo $?
1
プログラムを終了させないTCPサーバ
次は、せめて何度でもリクエストに答えられるように for
で繰り返しリクエストに応答します。
しかし、一度に1リクエストしか受け付けられません。その様子がわかるように処理にsleep処理を入れて、ふたつのクライアントから接続にいきます。
package main
import (
"fmt"
"io"
"net"
"time"
)
func main() {
ln, err := net.Listen("tcp", ":8080")
fmt.Println("TCP server is running on localhost:8080")
if err != nil {
panic(err)
}
for {
conn, err := ln.Accept()
if err != nil {
panic(err)
}
io.WriteString(conn, "processing...")
time.Sleep(3 * time.Second)
io.WriteString(conn, "done")
conn.Close()
}
}
片方の処理が終わってから、もう片方の処理が実行されることを確認できます。
複数リクエストを処理できるTCPサーバ
続いて、今度は複数リクエストを同時に処理できるように改良します。 実処理部分をgoroutineで別スレッド処理にすることで実現できるでしょう。
package main
import (
"fmt"
"io"
"net"
"time"
)
func main() {
ln, err := net.Listen("tcp", ":8080")
fmt.Println("TCP server is running on localhost:8080")
if err != nil {
panic(err)
}
for {
conn, err := ln.Accept()
if err != nil {
panic(err)
}
// goroutineでノンブロッキング処理
go func() {
io.WriteString(conn, "processing...")
time.Sleep(3 * time.Second)
io.WriteString(conn, "done")
conn.Close()
}()
}
}