在Golang Server中接受持久性tcp连接

时间:2016-10-22 09:23:11

标签: go tcp buffer telnet

我正在试验Go - 并且想创建一个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)))
        }
    }
}

2 个答案:

答案 0 :(得分:4)

循环的第二个例子已经是你想要的了。您只需根据需要进行循环读取(或者可能直到某些读/写超时或外部取消信号)。

但是它仍然有错误: TCP为您提供了一个字节流,不保证一侧的一次写入将在另一侧产生一次具有相同数据长度的读取。这意味着如果客户端写入PING\r\n,您在第一次阅读时仍然只能收到PI。您可以使用bufio.Scanner修复此问题,并始终阅读第一个换行符。

答案 1 :(得分:3)

不确定这是否是您正在寻找的。摘自net/http实施,包含net.TCPListener Accept方法。

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