From ef89ad1d3a599afbca4639171f69a54823e1a337 Mon Sep 17 00:00:00 2001 From: Shin Kojima Date: Wed, 25 Dec 2013 02:49:32 +0900 Subject: [PATCH] [ja] apply patch --- ja/ebook/08.1.md | 70 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/ja/ebook/08.1.md b/ja/ebook/08.1.md index bb05132f..4ae86ae9 100644 --- a/ja/ebook/08.1.md +++ b/ja/ebook/08.1.md @@ -226,6 +226,76 @@ Go言語ではnetパッケージの`DialTCP`関数によってTCP接続を一つ タスク処理を関数`handleClinet`に分離することで、一歩進んで並列実行を実現できるようになります。見た目にはあまりかっこ良くありません。`go`キーワードを追加することでサーバの並列処理を実現しました。この例からgoroutineの強力さを見ることができます。 +こう思う方もおられるかもしれません:このサーバはクライアントが実際にリクエストしたコンテンツを処理していない。もしもクライアントから異なるリクエストで異なる時刻形式を求められ、しかも長時間に渡る接続だった場合どうすればよいのか?と。その場合は以下をご覧ください: + + package main + + import ( + "fmt" + "net" + "os" + "time" + "strconv" + ) + + func main() { + service := ":1200" + tcpAddr, err := net.ResolveTCPAddr("tcp4", service) + checkError(err) + listener, err := net.ListenTCP("tcp", tcpAddr) + checkError(err) + for { + conn, err := listener.Accept() + if err != nil { + continue + } + go handleClient(conn) + } + } + + func handleClient(conn net.Conn) { + conn.SetReadDeadline(time.Now().Add(2 * time.Minute)) // set 2 minutes timeout + request := make([]byte, 128) // set maxium request length to 128KB to prevent flood attack + + for { + read_len, err := conn.Read(request) + + if err != nil { + if err != io.EOF { // ignore EOF since client might send nothing for the moment + fmt.Println(err) + break + } + + neterr, ok := err.(net.Error) + if ok && neterr.Timeout() { + fmt.Println(neterr) + break + } + } + + if read_len == 0 { + continue + } else if string(request) == "timestamp" { + daytime := strconv.FormatInt(time.Now().Unix(), 10) + conn.Write([]byte(daytime)) + } else { + daytime := time.Now().String() + conn.Write([]byte(daytime)) + } + + request = make([]byte, 128) // clear last read content + } + } + + func checkError(err error) { + if err != nil { + fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error()) + os.Exit(1) + } + } + +上の例では`conn.Read()`を使用してクライアントが送信するリクエストを絶え間なく読み込んでいます。クライアントとの長時間接続を保持しなければならないため、一度のリクエストを読み終わった後も接続を切断することはできません。`conn.SetReadDeadline()`はタイムアウトを設定しているので、一定時間内にクライアントからリクエストが送られなければ、`conn`は自動的に接続を切断します。下のforループは接続が切断されることによって抜け出します。注意しなければならないのは、`request`は新規に作成される際にflood attackを防止するため最大の長さを指定しなければならないということです;毎回リクエストが読み込まれ処理が完了する度にrequestを整理しなければなりません。なぜなら`conn.Read()`は新しく読み込んだ内容を元の内容の後にappendしてしまうかもしれないからです。 + ### TCP接続のコントロール TCPには多くの接続コントロール関数があります。我々が普段よく使うものは以下のいくつかの関数です: