[golang]TCPサーバーを作ろう!

こんにちは。KOUKIです。

とある企業でWeb系の開発エンジニアをしています。

今日は、golangで作るTCPサーバーのハンズオン記事を書きたいと思います!

TCPについては、こちらの記事がわかりやすいです。

関連記事

ワークスペースの準備

まずは、作業場を作りましょう!

TCPサーバーの実装

TCPサーバーを実装します。

リスナーの作成

最初に、netパッケージを活用してリスナーを作りましょう。

net.ListenTCPメソッドにて、リスナーを作ります。このリスナーは、クライアントとの通信コネクションを制御する様々なAPIを提供しています。

このリスナーの引数には、net.ResolveTCPAddrから作成したリッスン情報を渡します。ここでは、「XXXX:8080」の情報が渡されることになります。

そのためローカルで動かす場合は、クライアント側からは「localhost:8080」でコネクションを貼ることが可能になります。

コネクションを受けとる

次は、クライアント側からコネクションを受け取る処理を実装します。

リスナーから提供される機能の一つにAcceptTCPがあります。これによりクライアント側からのリクエストを受け取れるようになります。

ハンドラーを作成する

最後にハンドラーを実装しましょう。

クライアントからアクセスが発生したら「response from server」を返すようにしています。

一旦実装が完了しました。リファクタリングはあとで行うとして、今度はクライアント側を実装しましょう。

クライアントコードの実装

クライアント側は本記事の趣旨ではないので、ソースコードだけ記載しておきます。

動作確認

動作確認をしてみましょう。terminalを3つ開いてそれぞれ以下のコマンドを実行してください。

TCPサーバーは問題なく起動しました。2つのクライアントの内、一つはレスポンスが受け取れてますね。

しかし、もう一つのクライアントに関しては、処理が止まってしまいました。

複数リクエストを受け取れるようにする

実は、実装したTCPサーバーのコードには、バグがあります

一つのクライアントからリクエストを送ると、そのリクエストの処理が完了するまで処理をブロックするのです。その場所は、echoHandlerです。

この問題を解決するには、echoHandlerを別プロセスで実行してあげれば良さそうです。

Go言語には、goroutineというものがあります。goキーワードを関数の前に指定することで、並行処理を実装することができます。

これを使ってechoHandlerを別プロセスで起動しましょう。

これで、複数のリクエストを受け取れるTCPサーバーになりました。

ちなみに、こっちの書き方も好きです。

タイムアウトを設定する

便利なことに、リスナーが提供しているSetDeadlineメソッドでタイムアウトを設定できます。

サーバー起動後、10秒経過した時点でTCPサーバーが停止するようにしました。

キャンセル処理

contextパッケージのWithDeadlineを活用するとキャンセル処理を実装できます。

もともと実装していたソースコードも変えました。

cancelが呼ばれたらreceiveTCPConnection内に設定した、select文の<-ctx.Doneが呼ばれます。

OKですね。ちゃんとキャンセルされているようです。

cancelには、main関数を抜けたら確実に実行されるようにしたいので、deferをつけておきましょう。

皆さんの中には、「キャンセル処理ってあまり意味ないんじゃないか?」と思われる方もいるかもしれませんね。

くだんのreceiveTCPConnectionは、メインプロセスと同じプロセス上で動いているので、main関数を抜けたら処理が終了するからです。

しかし、goroutineを使ってサブプロセスで動かした場合は、メインプロセスが停止しても処理が残り続ける可能性があるので、それを防ぐという意味では有効だと思います。

おわりに

これまで見てきたとおり、Go言語では簡単にTCPサーバーを作ることができます。。

日本でもどんどん流行っていく言語だと思うので、Go言語は習得しておいてそんはないと思います!

それでは、また!

TCPサーバーコード

過去記事

オススメ書籍