2016-10-22 27 views
2

私はGoを試しています。私はtelnetでコマンドを送信し、応答を受け取ることができるTCPサーバを作りたいと思います。Golangサーバで永続的なtcp接続を受け入れる

const (
    CONN_HOST = "localhost" 
    CONN_PORT = "3333" 
    CONN_TYPE = "tcp" 
) 

func main() { 

    listener, err := net.Listen(CONN_TYPE, fmt.Sprintf("%s:%s", CONN_HOST, CONN_PORT)) 
    if err != nil { 
     log.Panicln(err) 
    } 

    defer listener.Close() 

    for { 
     conn, err := listener.Accept() 
     if err != nil { 
      log.Panicln(err) 
     } 

     go handleRequest(conn) 
    } 
} 

func handleRequest(conn net.Conn) { 
    buffer := make([]byte, 1024) 

    length, err := conn.Read(buffer) 
    if err != nil { 
     log.Panicln(err) 
    } 

    str := string(buffer[:length]) 

    fmt.Println(conn.RemoteAddr().String()) 
    fmt.Printf("Received command %d\t:%s\n", length, str) 

    switch str { 
     case "PING\r\n": 
     sendResponse("PONG", conn) 
     case "PUSH\r\n": 
     sendResponse("GOT PUSH", conn) 
    default: 
     conn.Write([]byte(fmt.Sprintf("UNKNOWN_COMMAND: %s\n", str))) 
    } 

    conn.Close() // closes the connection 
} 

func sendResponse(res string, conn net.Conn) { 
    conn.Write([]byte(res+"\n")) 
} 

上記のスニペットは、毎回接続を終了し、ターミナルセッションから私を蹴飛ばします。しかし、私が実際に望んでいるのは、より多くのI/O操作のために接続を開いたままにすることです。私が単にconn.Close()を取り除くと、サーバーはそれ以上の応答を得られないのでどこかにぶら下がっているように見えます。

私がこれを解決した方法は、QUIT\r\nメッセージを受信するまで終了しないように、私のhandleRequestメソッドを無限ループにすることです。これは適切なのでしょうか?それとも達成のための良い方法がありますか?

func handleRequest(conn net.Conn) { 
    for { 
     log.Println("Handling Request") 
     buffer := make([]byte, 1024) 

     length, err := conn.Read(buffer) 
     if err != nil { 
      log.Panicln(err) 
     } 

     str := string(buffer[:length]) 

     fmt.Println(conn.RemoteAddr().String()) 
     fmt.Printf("Received command %d\t:%s\n", length, str) 

     switch str { 
     case "PING\r\n": 
      sendResponse("PONG", conn) 
     case "PUSH\r\n": 
      sendResponse("GOT PUSH", conn) 
     case "QUIT\r\n": 
      sendResponse("Goodbye", conn) 
      conn.Close() 
     default: 
      conn.Write([]byte(fmt.Sprintf("UNKNOWN_COMMAND: %s\n", str))) 
     } 
    } 
} 
+1

はい、複数回読み取るために読み取りをループする必要があります。エラーが発生したときにパニックに陥らず、接続が終了するとプログラムをクラッシュさせる理由はありません。 – JimB

+1

これは正しい実装だと思います。ちょうどヒント:あなたが 'conn.Close()'を呼んだ後では、break文を呼び出す必要があります。それ以外の場合、forループは常に実行されます – Tinwor

+0

JimBありがとう。エラー時の再パニック - 私はまだ適切なエラーハンドラを構築していないので、私はdevでやっていました。そして、エラーを捕捉したかっただけです。ティンワ - 良い場所。 – Gravy

答えて

4

あなたの第二の例は、あなたが欲しいものをすでにあります。必要なだけループして読み込みます(または、おそらく読み込み/書き込みのタイムアウトまたは外部からの取り消し信号があるまで)。 TCPは、バイトのストリームを提供します。この場合、1つの側からの1つの書き込みが、同じデータ長の反対側の1つの読み出しを正確に生成することは保証されません。つまり、クライアントがPING\r\nを書き込むと、最初の読み取りではPIしか受け取れません。 bufio.Scannerを使用して修正でき、常に最初の改行まで読み取ることができます。

3

あなたが探しているものであるかどうか不明です。 net/httpの実装から、net.TCPListenerAcceptメソッドをラップします。

tcpKeepAliveListener{listener.(*net.TCPListener)}

type tcpKeepAliveListener struct { 
    *net.TCPListener 
} 

func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) { 
    tc, err := ln.AcceptTCP() 
    if err != nil { 
     return 
    } 
    tc.SetKeepAlive(true) 
    tc.SetKeepAlivePeriod(3 * time.Minute) 
    return tc, nil 
} 

参照してください:ループとLink 1 & Link 2

+1

私が望んでいたものではありませんが、非常に便利です。ありがとう – Gravy