最近在学计网,学到 tcp 三次握手四次挥手。书上说关闭连接(挥手)分为以下4个步骤:
- A 向 B 发送关闭报文。
- B 向 A 回复确认报文。A 收到报文后进入到半关闭状态,不再发送数据。
- B 回复后依然可以发送数据。在 B 发送完所有数据后会发送关闭报文。
- A 向 B 回复确认报文。B 收到报文后进入关闭状态,A 等待一段时间后也进入关闭状态。
这里的第 3 步还挺有意思的。想想也是,全双工的连接那自然需要两边都关闭才叫真的关闭。只有一边关了的话另一边肯定还能发数据。之前都是以为 tcp 连接断开了就没用了,平时写代码都是 defer conn.Close() 的。没想到关了连接之后还能读到数据。笔者又写了个 demo 试了一下,发现真的可以。代码如下:
// a.go 客户端
package main
import (
"net"
"os"
)
func main() {
// 2. 建立连接
c, _ := net.Dial("tcp", "127.0.0.1:9090")
defer c.Close()
// 4. 关闭写方向连接
conn := c.(*net.TCPConn)
conn.CloseWrite()
// 7. 打印收到的数据
conn.WriteTo(os.Stdout)
}
// b.go 服务端
package main
import (
"fmt"
"net"
"time"
)
func main() {
// 1. 监听端口
l, _ := net.Listen("tcp", "127.0.0.1:9090")
defer l.Close()
// 3. 建立连接
conn, _ := l.Accept()
defer conn.Close()
// 5. 等客户端半关连接
time.Sleep(100 * time.Millisecond)
// 6. 写数据
fmt.Fprintln(conn, "goodbye")
}
打开两个终端,先在一个终端执行 go run b.go,再在另一个终端执行 go run a.go。最终 a 这边会打印 "goodbye"。