对于 tcp 服务器,我们 ofter 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())
}
阿拉伯数字
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?
此答案假定程序的目标是将数据回显到客户端。 这个答案包括托雷克和利蒙的评论中的信息。
阅读的文档说
读取将高达 len(p( 字节读取到 p 中。它返回读取的字节数 (0 <= n <= len(p(( 和遇到的任何错误。即使 Read 返回 n
第二个程序可能无法读取所有数据,因为 Read 可以返回可用数据,而不是等待更多数据。 第二个程序可能不会写入所有数据,因为读取可能会返回数据和错误。
让我们专注于第一个程序,因为它更正确。程序可以在连接上进行多次读取和写入调用,但这不是重用连接。
最好在函数的开头defer c.Close()
,而不是在整个函数中散布对c.Close()
的调用。延迟可确保无论函数如何退出,连接都已关闭。
func handleConnection(c net.Conn) {
defer c.Close()
buf := make([]byte, 4096)
for {
n, err := c.Read(buf)
if err != nil || n == 0 {
break
}
n, err = c.Write(buf[0:n])
if err != nil {
break
}
}
fmt.Printf("Connection from %v closed.n", c.RemoteAddr())
}
读取可以返回一些数据和错误。在处理错误之前处理返回的任何数据:
func handleConnection(c net.Conn) {
defer c.Close()
buf := make([]byte, 4096)
for {
n, er := c.Read(buf)
if n > 0 {
_, ew := c.Write(buf[:n])
if ew != nil {
break
}
}
if er != nil {
break
}
}
fmt.Printf("Connection from %v closed.n", c.RemoteAddr())
}
使用 io。复制而不是在应用程序中编写复制逻辑。木头条。复制功能正确获取所有细节。另外,io。复制可以使用一些较低级别的优化将数据从一个连接复制到另一个连接。
func handleConnection(c net.Conn) {
defer c.Close()
io.Copy(c, c)
fmt.Printf("Connection from %v closed.n", c.RemoteAddr())
}
这两种方法执行不同操作:
第一种方法读取一些数据并将其写回,直到出现错误或读取 0 字节。
第二种方法读取一次,写回一次,然后关闭连接。
第一种方法不会"重用"连接。它正在实现一个简单的类似回声的协议,该协议一直运行到连接终止。第二种方法回显一次并关闭连接。