Edited at

GoでTCPコネクションはるよー

More than 1 year has passed since last update.


はじめに

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)
}