2016-10-22 183 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