■前置き
Goってのはマルチコア処理で効率的に同時アクセスをさばく、現時点で最強?のネットワークプログラミング言語ですので
その機能を最大限に発揮するスケルトンコードをチュートリアルで公開しているものと思っていました。
が、あまりにプリミティブな(基礎的な)ことしか頭に入ってこなかったので、仕方なく自作することにしました。
ゼロから作るのは非効率でしたので、参考コードとして前々回紹介した以下の二つのコードを使わせてもらいました。
golang socket server & client ping-pong demo
go server-clientアプリケーションの実装
■本題
複数クライアントからの同時アクセスを受け付けるサーバーとクライアントのコードを書いてみました。
足りない部分は公式ドキュメントを頼りに実装し、動作確認まで済ませました。
1.クライアントを起動するとサーバーに接続、クライアントで適当な文字列を打ち込むと、これをサーバーに送ります。
2.サーバーは複数のクライアントのセッションを張り続け、一つのクライアントから文字列が送られてきたら、同じ内容をそのクライアントだけに返します。
3.クライアントはサーバーから文字列を受け取ったら表示し、次の文字列の入力を待ちます。
4.サーバーはセッションが切れたら、クライアントからの送信待ちループを抜けてコネクションを閉じます(サーバーは終了せずに、次の接続を待機します)
server.go
package main import ( "bufio" "io" "log" "net" "strconv" "strings" ) func main() { port := 7777 SocketServer(port) } // const meesages const ( StopCharacter = "\r\n\r\n" ) // SocketServer start a server func SocketServer(port int) { listen, err := net.Listen("tcp4", ":"+strconv.Itoa(port)) defer listen.Close() if err != nil { log.Fatalf("Socket listen port %d failed,%s", port, err) } log.Printf("Begin listen port: %d", port) for { conn, err := listen.Accept() if err != nil { log.Fatalln(err) continue } go handler(conn) } } func handler(conn net.Conn) { defer conn.Close() var ( buf = make([]byte, 1024) r = bufio.NewReader(conn) w = bufio.NewWriter(conn) ) CLOOP: for { var received []string ILOOP: for { n, err := r.Read(buf) data := string(buf[:n]) switch err { case io.EOF: break CLOOP case nil: hasStopCharacter := false if strings.HasSuffix(data, StopCharacter) { hasStopCharacter = true data = strings.TrimSuffix(data, StopCharacter) } log.Println("Receive:", data) received = append(received, data) if hasStopCharacter { break ILOOP } default: log.Fatalf("Receive data failed:%s", err) return } } echo := strings.Join(received, "") w.Write([]byte(echo)) w.Flush() log.Printf("Send: %s", echo) } }
client.go
package main import ( "bufio" "fmt" "log" "net" "os" "strconv" "strings" ) func main() { var ( ip = "127.0.0.1" port = 7777 ) SocketClient(ip, port) } // const messages const ( Exit = "exit\r\n" StopCharacter = "\r\n\r\n" ) // SocketClient start client func SocketClient(ip string, port int) { addr := strings.Join([]string{ip, strconv.Itoa(port)}, ":") conn, err := net.Dial("tcp", addr) defer conn.Close() if err != nil { log.Fatalln(err) } for { inputln := WaitInputln() log.Printf("inputln: %s", inputln) if 0 == strings.Compare(inputln, Exit) { break } SendMessage(conn, inputln) WaitServerMessage(conn) } } // WaitInputln wait inupt line func WaitInputln() string { for { reader := bufio.NewReader(os.Stdin) fmt.Print("Input text: ") text, _ := reader.ReadString('\n') if len(text) > 1 { return text } } } // SendMessage sand message to server func SendMessage(conn net.Conn, message string) { jointed := strings.Join([]string{message, StopCharacter}, "") conn.Write([]byte(jointed)) log.Printf("Send: %s", message) } // WaitServerMessage wait server message func WaitServerMessage(conn net.Conn) { buff := make([]byte, 1024) n, _ := conn.Read(buff) log.Printf("Receive: %s", buff[:n]) }
Go の GC について気になったので調べた
かなりシンプルなアルゴリズムを使って、Java などと比べるとかなり停止時間が小さく、スループットも意識して改善が続けられているとのこと
5年前にはいくつかのケースでメモリリークが見つかっていたが、最近はそれらの問題は解決されているとのこと
postd.cc