【问题标题】:Accept a persistent tcp connection in Golang Server在 Golang Server 中接受持久的 tcp 连接
【发布时间】:2018-11-09 16:25:18
【问题描述】:

我正在试验 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"))
}

上面的 sn -p 每次都会关闭连接,把我踢出终端会话。但我真正想要的是能够保持连接打开以进行更多 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)))
        }
    }
}

【问题讨论】:

  • 是的,您需要循环阅读才能多次阅读。不要对错误感到恐慌,如果连接关闭,您的程序没有理由崩溃。
  • 我认为这是正确的实现。只是一个提示:在你调用 conn.Close() 之后记得调用一个 break 语句,否则 for 循环将永远运行
  • JimB 谢谢。对错误感到恐慌——我只是在开发中做,因为我还没有构建正确的错误处理程序。只是想捕获错误。 Tinwor - 好地方。

标签: go tcp buffer telnet


【解决方案1】:

您使用循环的第二个示例已经是您想要的。您只需循环并读取任意时间(或者可能直到某些读取/写入超时或外部取消信号)。

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

【讨论】:

    【解决方案2】:

    不确定这是否是您要查找的内容。取自 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

    【讨论】:

    • 不是我想要的,但非常有用。谢谢
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-30
    • 1970-01-01
    相关资源
    最近更新 更多