13
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-10-29

はじめに

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)
}
13
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?