こんにちは。KOUKIです。
とある企業でGo言語を主軸とした開発チームで働いています。
Go言語はシンプルな言語で、サーバーコードなどを組み込みのnetパッケージを使えば、簡単に実装できます。
そして、Go言語の顔である「Goroutine」を使うと、クライアントから複数のリクエストを受け取るサーバーコードを簡単に書けます!
事前準備
ワークスペースを作りましょう。
1 2 3 4 5 6 |
mkdir sample cd sample mkdir server mkdir client touch server/server.go touch client/client.go |
Goroutineを使う前
早速、コードを公開します。まずはGoroutineを使わないバージョンです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
// server/server.go package main import ( "fmt" "io" "log" "net" "time" ) // コネクションを裁く func handlerConn(c net.Conn) { defer c.Close() for { // リクエストを受け付けたらサーバー側に「response from server」を返す _, err := io.WriteString(c, "response from server\n") if err != nil { return } time.Sleep(time.Second) } } func main() { // tcp, かつ、localhost:8080でリクエストを受け取る listner, err := net.Listen("tcp", "localhost:8080") fmt.Println("Server Start") if err != nil { log.Fatal(err) } // ループで回して複数リクエストを受け取れるようにする for { // 接続 conn, err := listner.Accept() fmt.Println("Connect") if err != nil { log.Fatal(err) } handlerConn(conn) } } |
続いて、clientコードです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
// client/client.go package main import ( "io" "log" "net" "os" ) func response(dst io.Writer, src io.Reader) { if _, err := io.Copy(dst, src); err != nil { log.Fatal(err) } } func main() { // tcp, localhost:8080でリクエストを送る conn, err := net.Dial("tcp", "localhost:8080") if err != nil { log.Fatal(err) } // connからレスポンスを標準出力にだす response(os.Stdout, conn) } |
これらをterminalを開いて実行します。
1 2 3 |
# terminal1 go run server/server.go Server Start |
1 2 3 4 5 6 7 8 |
# terminal2 $ go run client/client.go response from server response from server response from server response from server response from server response from server |
サーバーからのレスポンスを標準出力に表示できました。ここまではOKです。
しかし、もう一つterminalを開いて、clientコードを実行します。
1 2 3 |
# terminal3 $ go run client/client.go |
何も表示されません。terminal2でリクエストを送っているため、terminal3の接続が待ちの状態になっているからです。
これをGoroutineを使って解決します。
サーバーコードにGoroutineを使う
サーバーコードを以下のように修正すればOKです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
func main() { // tcp, かつ、localhost:8080でリクエストを受け取る listner, err := net.Listen("tcp", "localhost:8080") if err != nil { log.Fatal(err) } // ループで回して複数リクエストを受け取れるようにする for { // 接続 conn, err := listner.Accept() if err != nil { log.Fatal(err) } go handlerConn(conn) <<<<< go keywordをつける } } |
handlerConnの呼び出しに「go」キーワードをつける、ただそれだけです。
一度、プログラムをCtrl+cで停止してから、同じように実行してみましょう。
1 2 3 |
# terminal1 $ go run server/server.go Server Start |
1 2 3 4 5 |
# terminal2 $ go run client/client.go response from server response from server response from server |
1 2 3 4 5 |
# terminal3 $ go run client/client.go response from server response from server response from server |
今度はリクエストを受け取れるようになりました!
面白いですね!
終わりに
Goroutineを使えば、このように色々な物を同時実行できるようになります。Go言語プログラマなら極め得ておくべき技術ですね^^
それでは、また!
最近のコメント