2016-10-22 123 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(),那麼服務器似乎掛在某個地方,因爲它沒有得到任何更多的響應。

我解決這個問題的方式是讓我的handleRequest方法無休止地循環,以便它永遠不會退出,直到它收到QUIT\r\n消息。這是否合適 - 還是有更好的實現方式?

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()'remeber調用break語句之後,否則for循環將永遠運行 – Tinwor

+0

JimB Thanks。重新犯錯誤 - 我只是在開發中,因爲我還沒有建立適當的錯誤處理程序。只是想捕捉錯誤。 Tinwor - 很好的地方。 – Gravy

回答

4

您與循環第二個例子已經是你想要的。您只需循環讀取(或者直到讀/寫超時或外部取消信號)。

但是它仍然有一個錯誤: TCP爲您提供了一個字節流,在這裏不能保證從一側寫入一個數據長度相同的另一側讀取。這意味着如果客戶寫入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