Goでdial

現実逃避気味にGoで簡単なエコーサーバ/クライアントを書いてみた。さすがソケットをそのままユーザに見せるのではなく、Plan9のdialライクなAPIになっている*1。こまかい文法についてはGoのページを参照してもらうとして、ちょっとコードを見ていこう。Plan 9版は第二回探検隊の資料に書いたので、興味があればそちらを参照してほしい。

ネットワーク関連のパッケージはnetになる。ソースコードはsrc/pkg/net以下にあるので、詳しい動作が知りたければそちらを参照したくなるだろう。

まずはクライアントから。net.Dialの引数にはホスト名を書いてもOKなので、netmkaddrは不要である。エラーコードを返すために、multiple return valuesを積極的に使っている。これはありかな。配列のスライスも使える。

package main

import (
	"fmt";
	"os";
	"net";
)

func main() {
	var buf [512]byte;
	s := "localhost";
	var rhost *string = &s;

	if len(os.Args) == 2 {
		rhost = &os.Args[1];
	}
	conn, err := net.Dial("tcp", "", *rhost + ":8007");
	defer conn.Close();
	if err != nil {
		fmt.Fprintf(os.Stderr, "%s\n", err.String());
		os.Exit(1);
	}

	for {
		nr, er := os.Stdin.Read(&buf);
		if er != nil {
			fmt.Fprintf(os.Stderr, "%s\n", er.String());
			break;
		}

		nw, ew := conn.Write(buf[0:nr]);
		if ew != nil {
			fmt.Fprintf(os.Stderr, "%s\n", ew.String());
			break;
		}

		ne, ee := conn.Read(&buf);
		if ee != nil {
			fmt.Fprintf(os.Stderr, "%s\n", ee.String());
			break;
		}

		os.Stdout.Write(buf[0:ne]);
	}
}

一方、サーバ側にannounceはない。いきなりnet.Listenでlistenerを得て、net.Acceptで待ち受けする。Plan 9ではlistenで待って、accept/hangupという流れで、ソケットよりもわかりやすい気がしたんだけど、なんでPlan 9と変えたんだろう?

acceptに成功したらワーカをgoroutineで動かす。go文はLimboのspawnに相当する。forkやpthreadよりもお気楽。

package main

import (
	"fmt";
	"os";
	"net";
)

func goecho(conn net.Conn) {
	var buf [512]byte;
	conn.Close();

	for {
		nr, er := conn.Read(&buf);
		if er != nil {
			if er != os.EOF {
				fmt.Fprintf(os.Stderr, "%s\n", er.String());
			}
			break;
		}

		nw, ew := conn.Write(buf[0:nr]);
		if nw != nr {
			fmt.Fprintf(os.Stderr, "%s\n", ew.String());
			break;
		}
	}
}

func main() {
	lis, err := net.Listen("tcp", ":8007");
	if err != nil {
		fmt.Fprintf(os.Stderr, "%s\n", err.String());
		os.Exit(1);
	}

	for {
		conn, err := lis.Accept();
		if err != nil {
			fmt.Fprintf(os.Stderr, "%s\n", err.String());
			os.Exit(1);
		}
		go goecho(conn);
	}
}

(追記:2009-11-22)「Goでエコーサーバーを書く」経由でdefer文の存在を知った。これは便利かもね。

*1:探検隊のとき、AT&Tだからdialなんだよねと突っ込まれたが、なるほどそうかも。お茶目だ。