Skip to content

4.22 用了 TCP 协议,数据一定不会丢吗?

常见的丢包场景

  • 建立连接时丢包
    • TCP连接用的全连接或者半连接队列满了会丢包
  • 网卡丢包
    • 如网线质量差、接触不良
    • 内核的RingBuffer缓冲区过小,发送数据太快导致溢出可能丢包
    • 网卡的传输速率不足,而网络传输速率太大,会丢包(一般常见于压测场景)
  • 接收缓冲区丢包
    • 接收缓冲区满了,出现了零窗口,但是相应的ACK包有延迟,发送方接收不及,仍然发送数据,会丢包
  • 发送链路丢包
    • 可以通过ping命令查看当前机器和目标机器是否有丢包行为
    • 通过mtr命令可以查看你的机器和目的机器之间的每个节点的丢包情况。
      • mtr -r 目标域名/IP (r表示report,以报告的形式打印结果)
      • 可以通过 -u 指定使用udp 包而不是ICMP包(因为有些机器不允许ICMP)
      • 两者结合分析效果更好
  • 中转服务丢包

mtr-udp

还有个小细节,Loss那一列,我们在 icmp 的场景下,关注最后一行,如果是 0%,那不管前面 loss 是 100% 还是 80% 都无所谓,那些都是节点限制导致的虚报

但如果最后一行是 20%,再往前几行都是 20% 左右,那说明丢包就是从最接近的那一行开始产生的,长时间是这样,那很可能这一跳出了点问题。如果是公司内网的话,你可以带着这条线索去找对应的网络同事。如果是外网的话,那耐心点等等吧,别人家的开发会比你更着急。

图片

TCP 协议一定不会丢包吗

TCP 保证的可靠性,是传输层的可靠性。也就是说,TCP 只保证数据从 A 机器的传输层可靠地发到 B 机器的传输层。

至于数据到了接收端的传输层之后,能不能保证到应用层,TCP 并不管。

假设现在,我们输入一条消息,从聊天框发出,走到传输层 TCP 协议的发送缓冲区,不管中间有没有丢包,最后通过重传都保证发到了对方的传输层 TCP 接收缓冲区,此时接收端回复了一个ack,发送端收到这个ack后就会将自己发送缓冲区里的消息给扔掉。到这里 TCP 的任务就结束了。

TCP 任务是结束了,但聊天软件的任务没结束。

聊天软件还需要将数据从 TCP 的接收缓冲区里读出来,如果在读出来这一刻,手机由于内存不足或其他各种原因,导致软件崩溃闪退了。

发送端以为自己发的消息已经发给对方了,但接收端却并没有收到这条消息。

于是乎,消息就丢了,虽然概率很小。

这类丢包问题怎么解决?

大家有没有发现,有时候我们在手机里聊了一大堆内容,然后登录电脑版,它能将最近的聊天记录都同步到电脑版上。也就是说服务器可能记录了我们最近发过什么数据,假设每条消息都有个 id,服务器和聊天软件每次都拿最新消息的 id进行对比,就能知道两端消息是否一致,就像对账一样。

对于发送方,只要定时跟服务端的内容对账一下,就知道哪条消息没发送成功,直接重发就好了。

如果接收方的聊天软件崩溃了,重启后跟服务器稍微通信一下就知道少了哪条数据,同步上来就是了,所以也不存在上面提到的丢包情况。

可以看出,TCP 只保证传输层的消息可靠性,并不保证应用层的消息可靠性。如果我们还想保证应用层的消息可靠性,就需要应用层自己去实现逻辑做保证。

那么问题叒来了,两端通信的时候也能对账,为什么还要引入第三端服务器?

主要有三个原因。

  • 第一,如果是两端通信,你聊天软件里有1000个好友,你就得建立1000个连接。但如果引入服务端,你只需要跟服务器建立1个连接就够了,聊天软件消耗的资源越少,手机就越省电
  • 第二,就是安全问题,如果还是两端通信,随便一个人找你对账一下,你就把聊天记录给同步过去了,这并不合适吧。如果对方别有用心,信息就泄露了。引入第三方服务端就可以很方便的做各种鉴权校验。
  • 第三,是软件版本问题。软件装到用户手机之后,软件更不更新就是由用户说了算了。如果还是两端通信,且两端的软件版本跨度太大,很容易产生各种兼容性问题,但引入第三端服务器,就可以强制部分过低版本升级,否则不能使用软件。但对于大部分兼容性问题,给服务端加兼容逻辑就好了,不需要强制用户更新软件。

正在精进