golang中如何管理Accept()返回的连接?

对于 tcp 服务器,我们通常使用 Listen() 和 Accept()。


func main() {

    ln, err := net.Listen("tcp", ":6000")

    if err != nil {

        fmt.Println(err)

        os.Exit(1)

    }


    for {

        conn, err := ln.Accept()

        if err != nil {

            fmt.Println(err)

            continue

        }


        go handleConnection(conn)

    }

}

但是有两种方法可以处理 Accept 返回的连接:


1


func handleConnection(c net.Conn) {

    buf := make([]byte, 4096)


    for {

        n, err := c.Read(buf)

        if err != nil || n == 0 {

            c.Close()

            break

        }

        n, err = c.Write(buf[0:n])

        if err != nil {

            c.Close()

            break

        }

    }

    fmt.Printf("Connection from %v closed.\n", c.RemoteAddr())

}

2


func handleConnection(c net.Conn) {

    defer c.Close()

    buf := make([]byte, 4096)   


    n, err := c.Read(buf)

    if err != nil || n == 0 {

        c.Close()

        break

    }

    n, err = c.Write(buf[0:n])

    if err != nil {

        c.Close()

        break

    }

    fmt.Printf("Connection from %v closed.\n", c.RemoteAddr())

}

这两种方法正确吗?似乎方法 1 重用了连接。但是如何向客户端指示 EOF?


繁星淼淼
浏览 359回答 2
2回答

皈依舞

此答案假定程序的目标是将数据回显给客户端。该答案包括torek和limon评论中的信息。Read的文档说Read 最多将 len(p) 个字节读入 p。它返回读取的字节数 (0 <= n <= len(p)) 和遇到的任何错误。即使 Read 返回 n < len(p),它也可能在调用期间使用所有 p 作为暂存空间。如果某些数据可用但 len(p) 字节不可用,则 Read 通常会返回可用的数据,而不是等待更多数据。第二个程序可能不会读取所有数据,因为 Read 可以返回可用数据而不是等待更多数据。第二个程序可能不会写入所有数据,因为 read 可以返回数据和错误。让我们关注第一个程序,因为它更正确。该程序可以在一个连接上进行多次读取和写入调用,但这不是重用连接。最好defer c.Close()在函数的开头而不是在c.Close()整个函数中散布调用。defer 确保无论函数如何退出,连接都会关闭。func handleConnection(c net.Conn) {&nbsp; &nbsp; defer c.Close()&nbsp; &nbsp; buf := make([]byte, 4096)&nbsp; &nbsp; for {&nbsp; &nbsp; &nbsp; &nbsp; n, err := c.Read(buf)&nbsp; &nbsp; &nbsp; &nbsp; if err != nil || n == 0 {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; n, err = c.Write(buf[0:n])&nbsp; &nbsp; &nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }&nbsp; &nbsp; fmt.Printf("Connection from %v closed.\n", c.RemoteAddr())}读取可以返回一些数据和错误。在处理错误之前处理返回的任何数据:func handleConnection(c net.Conn) {&nbsp; &nbsp; defer c.Close()&nbsp; &nbsp; buf := make([]byte, 4096)&nbsp; &nbsp; for {&nbsp; &nbsp; &nbsp; &nbsp; n, er := c.Read(buf)&nbsp; &nbsp; &nbsp; &nbsp; if n > 0 {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; _, ew := c.Write(buf[:n])&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if ew != nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; if er != nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }&nbsp; &nbsp; fmt.Printf("Connection from %v closed.\n", c.RemoteAddr())}使用io.Copy而不是在应用程序中编写复制逻辑。io.Copy 函数可以正确获取所有细节。此外,io.Copy 可以使用一些较低级别的优化来将数据从一个连接复制到另一个连接。func handleConnection(c net.Conn) {&nbsp; &nbsp; defer c.Close()&nbsp; &nbsp; io.Copy(c, c)&nbsp; &nbsp; fmt.Printf("Connection from %v closed.\n", c.RemoteAddr())}

莫回无

这两种方法做不同的事情:第一种方法读取一些数据并将其写回,直到出现错误或读取到 0 个字节。第二种方法读取一次,将其写回,然后关闭连接。第一种方法不会“重用”连接。它正在实现一个简单的类似回声的协议,该协议一直运行到连接终止。第二种方法回显一次并关闭连接。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go