在Golang服务器中接受持久的TCP连接

5

我正在尝试使用Go语言,想要创建一个TCP服务器,以便可以通过Telnet连接发送命令并接收响应。

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() 后,记得调用 break 语句,否则 for 循环将永远运行。 - Tinwor
JimB谢谢。关于错误恐慌-我只是在开发中这样做,因为我还没有建立适当的错误处理程序。我只是想捕获错误。Tinwor-很好的发现。 - Gravy
2个回答

5
你的第二个带循环的例子已经是你想要的。你只需要循环并读取所需数量的数据(或者直到达到某个读/写超时或外部取消信号)。
然而,代码中仍存在一个错误: 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
}

参考: 链接1链接2

该内容与IT技术有关。

1
不完全是我想要的,但非常有用。谢谢 - Gravy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接