一次Mac上假连接问题的追溯

问题引出

前几天听一位客户端同事报Mac上切换网络时会出现假连接现象,需要服务器配合调整心跳策略,以减轻对用户的影响。听到这个现象挺好奇的,原因在于:

  • 客户端有监听网络变化、自动重新建立连接的机制,应该不会出现假连接才对;
  • 服务器与客户端之间本身有心跳机制,之前设计的是由服务器下发,1分钟一次,两个心跳未收到客户端就会认为超时,走重连逻辑;

据同事描述,有些切换网络的场景应用层是监听不到事件的,导致连接没有走重连逻辑。那具体什么场景监听不到呢?

问题确认

我主要测试了两个场景:

  1. 先关闭wifi再开启;
  2. 从一个wifi切换到另一个wifi;

前一个场景未发现问题,第二个场景复现了问题:

一次Mac上假连接问题的追溯_第1张图片

当我从家里的wifi “一朵霸王花" 切到 手机热点”nova 7 SE 5G“时, 对应云会议的IM中出现了一个端发消息另一个端收不到的Case‘:
一次Mac上假连接问题的追溯_第2张图片
一次Mac上假连接问题的追溯_第3张图片

*说明:上面第一个图是手机端在12:00发送了7、8、9三条消息; 第二个图是同帐号的Mac在12:01却没有收到对应的消息; *

虽然复现了问题,不过还在想:是不是我们云会议产品做的不到位呢?那就也测下微信表现。

一次Mac上假连接问题的追溯_第4张图片
一次Mac上假连接问题的追溯_第5张图片

结果在微信上也复现了此问题:上面第一个截图是手机微信,在11:34发送了a、b、c、d、e、f、g七个字母,但第二个截图的Mac版微信也没有收到。

不论是云会议,还是微信,均没有弹出网络异常之类的提示,在应用层看来,网络连接是正常的。

由此,基本可以确认,Mac上网络切换时确实会出现应用层监听不到网络变化的问题,客户端同事所言非虚。

但是为什么会导致假连接呢?我不由得想探究下网络连接的原理,以及平时一些常见的关于网络方面的疑惑,诸如:

  • 网络连接的本质倒底是什么?
  • 切换网络为什么会造成假连接?
  • 断网恢复后连接是否还可以用?

问题追溯

网络连接的本质是什么

提到连接,直观的认识可能如下图,两台主机在网络上建立了一条类似于物理世界的连线:
一次Mac上假连接问题的追溯_第6张图片

但实际上TCP三次握手后建立的连接可能是这样:
一次Mac上假连接问题的追溯_第7张图片

  • 连接两端的主机各自维护了一个IP四元组,一台主机上会有很多连接,IP四元组则能唯一识别一个连接;
  • 当发送数据时,在数据包里携带IP四元组信息,能唯一标识是由哪条连接发出的数据;
  • 当收到数据包时,通过数据包的IP四元组信息来识别所属连接,进而交给指定的连接来处理;
  • 四元组中只要有一个信息对不上,就表示这个连接不存在,或者说,这个连接已经断开,此时数据包会被丢弃,并发送一个RST告诉对端断开连接;

由此可见,所谓的网络连接,只是连接两端的主机双方各自维护的一个IP四元组信息,可以理解为我们在内存中维护的一个结构体变量。

切换网络为什么会导致假连接?

网络变化一般有几种情况:

  • 网络类型发生变化,像手机上的 从wifi切4G或4G切wifi;
  • 网络状态发生变化,只是从网络连接到网络断开,或者反过来,网络类型和所在网络并未变化;
  • 同一类型的网络间发生切换,如:从一个Wifi切到另一个Wifi;

前两种情况系统都是有事件通知到应用层的,对于长连接应用来说,监听到后一般都会选择断开重连,所以问题不大。

同时这个做法也间接说明了一点:操作系统处理网络变化的方式是抛出事件让各个应用自己处理,系统本身并不会在网络变化时直接干预各应用程序建立的网络连接状态。

这里有一个疑问:为什么从断开到连接(或者从Wifi到4G)都要做断开重连的操作呢,既然网络状态已经正常,直接用原来的连接收发数据包是否可以?

回答这个问题,可能要分情况说明:

  • 当网络类型变化时,一定是不行的。原因在于本地IP发生了变化,新的IP并没有建立到远端的IP四元组连接,而旧连接维护的还是原IP的四元组,但原IP已经断网是无法收发数据包的;
  • 单纯网络状态发生变化时,IP地址一般是不变的,但连接也不一定能用,本文后续部分再详细讨论;
  • 对于第三种,其实和第一种类似,一个wifi切到另一个wifi,本地IP地址也是会变化的;

所以这个问题中切换Wifi的场景,其实是在切换网络,并会引起IP地址变化,需要应用程序做断开重连操作。但由于监听不到系统的网络变化通知,导致仍然在原连接上接收消息,最终收不到任何消息出现假连接问题。

那这个假连接会持续多久呢?

  • 据实际观察,公司的云会议客户端大概两分钟能感知到并恢复正常,是靠客户端的心跳过期检测来感知到的(1分钟一个心跳包,2个心跳未收到认为超时);
  • 微信大概1分多能感知到,应该也是基于类似的心跳检测机制;

心跳本质上就是发送数据包,也就是说,需要靠真实的数据传输才能检测连接状态。那如果没有数据传输呢?还能监测到网络已经断开吗?

TCP长连接有一个KeepAlive机制,如果开启则会持续一段时间后,主动发送探测报文来保活,如果连续探测数次都没有响应,则认为此TCP连接已死,主动关闭连接。
不过,TCP默认的KeepAlive触发时间为7200秒,也就是至少两小时以上才可以发现一个死亡连接。

如果没有KeepAlive机制呢?既没有数据传输,又没有keepalive,理论上双方的TCP连接将一直保持存在,是感知不到网络断开的状态的,这个结论可能与大多数程序猿的认知不一样。

断网恢复后旧的连接是否可用?

先说明下前提,这里讨论的是网络恢复后本地IP地址没有发生变化的场景,也就是回答上面跳过的第二种情况。

与切换网络场景明显的区别在于:连接的IP四元组可能还是有效的。这里的关键要看断开期间是否有数据传输。

Case1. 如果断开期间,服务器与客户端之间有数据传输,此时服务器发送的数据包是收不到客户端Ack的。由于TCP层是可靠协议,会尝试重传,当尝试一定次数仍然失败,则会停止重传,并断开TCP连接。
所以这期间如果客户端恢复了网络,要区分服务器连接是否还存在两种细分场景:

  • 如果客户端恢复网络时服务器连接已经不在,那么客户端发送的数据包到达服务器后,发现没有对应的IP四元组,则会收到服务器发的一个RST包,让客户端断开重新连接;
  • 如果服务器重传报文的过程中,客户端恢复了网络,客户端可以正常接受服务器的报文,并回执Ack,此时双方的TCP连接依然存在,相当于什么事都没发生;

Case2. 如果断开期间,服务器与客户端之间没有数据传输,就和上一个话题中讨论的一样,得看KeepAlive机制是否检测到连接已经断掉,断掉了就只能重连,没断掉时恢复了网络就能继续使用;

如何解决问题

对于网络从一个wifi切到另一个wifi这种场景,由于操作系统没有明确的事件通知,那就只能靠心跳来主动检测。

目前是有心跳超时检测机制的,我们能想到的办法:只能尝试缩短心跳时间,尽可能快的感知到连接状态问题来采取操作,不过心跳这种方法注定了只能缓解,无法根除问题。

有的同学可能会有疑问:TCP不是可靠协议吗?为何不能保证连接正常?

  • TCP的可靠在于数据传输可靠,有重试和Ack机制来保证数据必达,但是对于连接状况,TCP协议也得靠数据传输才能感知;
  • 如果没有数据传输,TCP自己也无从知道对方是否还在线,毕竟连接是虚拟的;

以上纯属个人理解,如果路过的大神有发现不对的还请帮忙指正,非常感谢!

参考资料

  • https://reid00.github.io/2022/03/18/%E6%96%AD%E7%94%B5%E5%90%8ETCP%E8%BF%98%E5%9C%A8%E5%90%97/

你可能感兴趣的:(实践案例,故障剖析,网络)