图片-稻子网
图片-稻子网
图片-稻子网
图片-稻子网

魔兽世界mac版与服务器断开连接-4个实验彻底了解TCP连接的断开

前言

看到这个标题,你可能会说TCP连接的建立和断开我很熟悉。那不是三次握手四次挥手吗?等一下,你可以试着先在脑海中回答这些问题:

这不是面试,而是一个实际问题。至于问题是什么,我先说一下,本文不作解答。后面会专门写一篇文章说问题是什么,所以我说的是实际问题。在此之前,先了解一下理论。

正常断开

由浅入深,先了解TCP连接在正常情况下是如何断开的。下图是经典的TCP三向握手和四次挥手(来自《TCP/IP详解1》)

图片[1]-魔兽世界mac版与服务器断开连接-4个实验彻底了解TCP连接的断开-稻子网

在我们的电脑上,你可以用它来快速启动一个http服务(http也是基于TCP协议的),例如:

python -m SimpleHTTPServer 20880

然后使用 nc 或者这两个命令创建一个 TCP 连接。比如我测试使用nc创建连接

nc -v ip port

到 ip 端口 [tcp/*]! 表示连接成功

图片[2]-魔兽世界mac版与服务器断开连接-4个实验彻底了解TCP连接的断开-稻子网

我们如何观察这种联系?可以通过或者lsof查看这个“连接”,这里我用的是lsof(mac和Linux系统的命令不一样,用起来有点别扭)

lsof -i:20880

图片[3]-魔兽世界mac版与服务器断开连接-4个实验彻底了解TCP连接的断开-稻子网

客户端和服务端都会占用一个端口,但是服务端端口是固定的,客户端端口是随机的。

如果我们想看看在TCP连接和断开连接过程中如何检查握手和挥手的TCP数据包?你可以使用命令

三握手

tcpdump -A -vv -i any -S host 10.179.245.95

为了方便观看,把上面的经典图片放在一起

图片[4]-魔兽世界mac版与服务器断开连接-4个实验彻底了解TCP连接的断开-稻子网

这里需要提到的参数是-S。如果不加-S参数,第三次握手的ack=1和书上的理论不太一样。事实上,这只是一个简化的显示。如果要查看实际值,则需要 Plus -S

此处标记 [S]/[S.]/[.]

挥手四次

命令和三向握手一样,我们抓到了如下的挥手数据

图片[5]-魔兽世界mac版与服务器断开连接-4个实验彻底了解TCP连接的断开-稻子网

这张图有点奇怪。四次挥手变成了三下。这其实是TCP协议实现的问题。如果第二波和第三波之间没有数据发送,那么被动断开的一方可能会将第二个ACK和第三个FIN合并为一个波。

当然,我也抓到了四波正常,看起来是这样的

图片[6]-魔兽世界mac版与服务器断开连接-4个实验彻底了解TCP连接的断开-稻子网

异常断线

上面已经说了这么多,现在我们开始进入这个话题。

谁发起了TCP连接的断开

我们来思考一个问题:TCP连接的断开是谁发起的?程序本身还是操作系统?

我们来看一个非常简单的TCP连接创建和断开代码

tcpAddr, _ := net.ResolveTCPAddr("tcp", "127.0.0.1:20880")
conn, err := net.DialTCP("tcp", nil, tcpAddr)
if err != nil {
 fmt.Println("Client connect error ! " + err.Error())
 return
}
defer func() {
 err := conn.Close()
 fmt.Println("Client connect closed !")
 if err != nil {
  fmt.Println(err)
 }
}()
fmt.Println(conn.LocalAddr().String() + " : Client connected!")
time.Sleep(10 * time.Second)

运行后效果如下,也符合我们的预期:程序打印!时,可以看到连接,打印时!,连接断开

图片[7]-魔兽世界mac版与服务器断开连接-4个实验彻底了解TCP连接的断开-稻子网

如果我们在断开连接之前使用kill -9 强行杀死进程会怎样?(这里我用两台电脑测试)

图片[8]-魔兽世界mac版与服务器断开连接-4个实验彻底了解TCP连接的断开-稻子网

我们发现没有执行conn.Close(),但是还是发生了四波!

查阅资料后,得出以下结论:

a, b 两个正常连接的对等进程。如果b进程在没有调用close的情况下异常终止,那么内核OS会替你发送FIN包

断电/断网时连接如何断开

通过上面的实验,我们发现即使进程异常终止,操作系统也会帮助发起四波手。

但如果是停电或网络断开,操作系统无法为您做。那会发生什么?为了方便测试,这里使用两台电脑,连接和断开来模拟断网和断电的情况。

可以肯定的是魔兽世界mac版与服务器断开连接,网络已断开连接。断电后,连接不会立即断开,那么后续的连接会断开吗?我们把它分为以下几种情况

断网时有数据传输

如果断网时有数据要发送,会因为收不到ACK而重试,但不会无限重试。重传一定次数后,如果仍然没有返回确认响应,则判断为网络或对端主机发生异常,连接被强行关闭。这时候关机直接关闭,不用挥手(数据发不出去,你还是摆摆手),Linux下的设置是

最小重传时间200ms,最大重传时间120s,重传次数15次

断开连接时无数据传输

如果网络断开时没有数据传输,仍然取决于TCP连接是否打开。TCP的介绍如下:

打开

操作系统中有几种参数控制的配置:

在Linux上可以通过以下文件查看

cat /proc/sys/net/ipv4/tcp_keepalive_time
cat /proc/sys/net/ipv4/tcp_keepalive_intvl
cat /proc/sys/net/ipv4/tcp_keepalive_probes

图片[9]-魔兽世界mac版与服务器断开连接-4个实验彻底了解TCP连接的断开-稻子网

如果你看这个默认值魔兽世界mac版与服务器断开连接,在你开始工作前2小时不会有数据传输!

在 Go 中,只有两个参数可以设置:

conn.SetKeepAlive(true)
conn.SetKeepAlivePeriod(5 * time.Second)

第二个源代码是这样的:

func setKeepAlivePeriod(fd *netFD, d time.Duration) error {
 // The kernel expects seconds so round to next highest second.
 secs := int(roundDurationUp(d, time.Second))
 if err := fd.pfd.SetsockoptInt(syscall.IPPROTO_TCP, sysTCP_KEEPINTVL, secs); err != nil {
  return wrapSyscallError("setsockopt", err)
 }
 err := fd.pfd.SetsockoptInt(syscall.IPPROTO_TCP, syscall.TCP_KEEPALIVE, secs)
 runtime.KeepAlive(fd)
 return wrapSyscallError("setsockopt", err)
}

参数是同时设置的,不能设置

做一个简单的测试:打开连接后,没有数据发送,断网,可以看到心跳包。一段时间后,连接设置为状态

图片[10]-魔兽世界mac版与服务器断开连接-4个实验彻底了解TCP连接的断开-稻子网

关闭

关闭后,如果没有数据传输,连接永远不会断开

断开连接后重新启动和恢复

考虑另一种情况。如果连接建立后没有数据传输,最后的网络也断了,这时候如果重启程序,恢复网络,这个连接还能用吗?

如果重启后还是不发送数据,那么连接好像是可用的,因为他们不知道对方是什么,但是如果你这时候发送一点数据,你会发现会发送一个RST ,然后它就会断开连接。连接的

图片[11]-魔兽世界mac版与服务器断开连接-4个实验彻底了解TCP连接的断开-稻子网

总结

除了正常情况,本文从TCP连接断开的角度结合实验给出了一些结论:

如果一方向另一方发送 RST 数据包,则会强制对方断开连接

原文链接:

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片