はじめに
Go言語の勉強し始めて2・3日のインフラエンジニアがなんとなく監視をテーマに選んで遊んで見る会。
今回はGo言語を用いてTCPコネクションをざっくり張ってみます。
要はServer-Client型のぷろぐらむです。
ソースコードも公開しておきます。誰得かはしらない。
気が向いたらアップデートされたりもするかも。
Server コード
まずはnet.Listen
を利用してListenポートを開きます。
既にこのポートが別のアプリケーションで利用されていると即死します。
func CreateListener() net.Listener {
ln, err := net.Listen("tcp", ":8081")
if err != nil {
log.Fatal(err)
}
return ln
}
その後通信要求に応じてコネクション(net.Conn
)を開きます。
func ListenWorker(ln net.Listener) { conn, err := ln.Accept()
if err != nil {
log.Fatal(err)
}
defer conn.Close()
複数のコネクションを受ける場合はドキュメントにあるようにgoroutineに処理を放り投げるのが良いみたいですね。
deferしておくと終了時に勝手にコネクション閉じてくれるので、使えそうなら入れておきましょう。
今回は1対1の処理構造なので処理が済んだら終わりです。生成処理をfunctionにいれたりすると、function終わった瞬間に即死する気がする。
そしてメッセージの受信待ち、受信後の処理を定義します。
今回は改行を区切り文字として定義してるので複数行届いても1行ずつ処理されます。
for {
message, err := bufio.NewReader(conn).ReadString('\n')
if err != nil {
log.Fatal(err)
}
log.Print("Message Received:", string(message))
newmessage := strings.ToUpper(message)
conn.Write([]byte(newmessage + "\n"))
}
}
受信したら大文字にしてClientに返すってだけの処理です。
現状コネクション制御が全く入っていないので、Clientが死ぬと暴走する危険なやつになってます。
あとはmainで動かして終わりです。
Workerを分離したのはgoroutine使って複数Clientからの接続を後々試すために分けておいただけでした。
func main() {
// Create Listen port and accept to connection.
log.Print("Launching server...")
ln := CreateListener()
ListenWorker(ln)
}
Client コード
ClientはServerへの発呼とデータの送受信を行う処理がメインですね。
自分がやりたいことだけ書けばいいのでサーバ側に比べると楽。
とりあえずコネクション貼っちゃいましょう。
net.Dialの時点でサーバに接続しに行くので、ここでエラー処理をしないと接続失敗しても動いている変なやつになります。
func MakeConnection(server, port string) net.Conn {
conn, err := net.Dial("tcp", server+":"+port)
if err != nil {
log.Fatal(err)
}
return conn
}
ここでは標準入力から何らかの入力を得てそれをサーバへ流す仕組みにします。
func GetInput() string {
for {
reader := bufio.NewReader(os.Stdin)
fmt.Print("Input text: ")
text, _ := reader.ReadString('\n')
if len(text) > 1 {
return text
}
}
}
取得した入力を送信する処理と、終了用にexitコマンドを定義してあります。
もっといろいろ増えそうならコマンド処理の部分はFunctionにしてもいいかなって思ったけどサボった。
Serverからの応答待ちと表示処理も一緒にやっちゃってます。
func PublishMessage(conn net.Conn) {
Loop:
for {
// Read in input from stdin
text := GetInput()
// If the input is "exit", we will stop the loop and end client.
switch {
case text == "exit\n":
log.Print("Entering exit command. EXIT.")
break Loop
}
log.Print("Will send text: " + text)
// Send to socket
conn.Write([]byte(text + "\n"))
// Listen for reply
message, _ := bufio.NewReader(conn).ReadString('\n')
log.Print("Message from server: " + message)
}
}
実際にやりたい処理のための基盤は大体出来たので、粗も多いけど今回はこのくらいで。
func main() {
// connect to this socket
conn := MakeConnection("127.0.0.1", "8081")
defer conn.Close()
log.Print("Connected.")
PublishMessage(conn)
}