如何管理 golang 中 Accept() 返回的连接?
How to manage connections returned by Accept() in golang?
对于 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?
这两种方法做不同的事情:
第一种方法读取一些数据并将其写回,直到出现错误,或者直到读取到 0 个字节。
第二种方法读取一次,写回,然后关闭连接。
第一种方法不"reuse"连接。它正在实施一个简单的类似回声的协议,该协议一直运行到连接终止。第二种方法回显一次并关闭连接。
此答案假定程序的目标是将数据回显给客户端。此答案包含 torek 和 limon 评论中的信息。
Read 的文档说
Read reads up to len(p) bytes into p. It returns the number of bytes read (0 <= n <= len(p)) and any error encountered. Even if Read returns n < len(p), it may use all of p as scratch space during the call. If some data is available but not len(p) bytes, Read conventionally returns what is available instead of waiting for more.
第二个程序可能无法读取所有数据,因为读取可以 return 可用数据而不是等待更多数据。第二个程序可能不会写入所有数据,因为读取可以 return 数据和错误。
让我们关注第一个程序,因为它更正确。该程序可以对一个连接进行多次读取和写入调用,但这不是重用该连接。
在函数的开头 defer c.Close()
比在整个函数中分散调用 c.Close()
更好。 defer 确保连接关闭,无论函数如何退出。
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())
}
读取可以return一些数据和错误。在处理错误之前处理 returned 的任何数据:
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.Copy而不是在应用程序中编写复制逻辑。 io.Copy 函数获取所有细节。此外,io.Copy 可以使用一些较低级别的优化来将数据从一个连接复制到另一个连接。
func handleConnection(c net.Conn) {
defer c.Close()
io.Copy(c, c)
fmt.Printf("Connection from %v closed.\n", c.RemoteAddr())
}
对于 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?
这两种方法做不同的事情:
第一种方法读取一些数据并将其写回,直到出现错误,或者直到读取到 0 个字节。
第二种方法读取一次,写回,然后关闭连接。
第一种方法不"reuse"连接。它正在实施一个简单的类似回声的协议,该协议一直运行到连接终止。第二种方法回显一次并关闭连接。
此答案假定程序的目标是将数据回显给客户端。此答案包含 torek 和 limon 评论中的信息。
Read 的文档说
Read reads up to len(p) bytes into p. It returns the number of bytes read (0 <= n <= len(p)) and any error encountered. Even if Read returns n < len(p), it may use all of p as scratch space during the call. If some data is available but not len(p) bytes, Read conventionally returns what is available instead of waiting for more.
第二个程序可能无法读取所有数据,因为读取可以 return 可用数据而不是等待更多数据。第二个程序可能不会写入所有数据,因为读取可以 return 数据和错误。
让我们关注第一个程序,因为它更正确。该程序可以对一个连接进行多次读取和写入调用,但这不是重用该连接。
在函数的开头 defer c.Close()
比在整个函数中分散调用 c.Close()
更好。 defer 确保连接关闭,无论函数如何退出。
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())
}
读取可以return一些数据和错误。在处理错误之前处理 returned 的任何数据:
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.Copy而不是在应用程序中编写复制逻辑。 io.Copy 函数获取所有细节。此外,io.Copy 可以使用一些较低级别的优化来将数据从一个连接复制到另一个连接。
func handleConnection(c net.Conn) {
defer c.Close()
io.Copy(c, c)
fmt.Printf("Connection from %v closed.\n", c.RemoteAddr())
}