toppic
当前位置: 首页> 玄幻小说> 用了TCP协议,就一定不会丢包吗?

用了TCP协议,就一定不会丢包吗?

2023-07-18 07:39:35
安全作业管理平台

数据从发送端到接收端,链路很长,任何一个地方都可能发生丢包,几乎可以说丢包不可避免。

数据包的发送流程

首先,我们两个手机的绿皮聊天软件客户端,要通信,中间会通过它们家服务器。大概长这样。

聊天软件三端通信

但为了简化模型,我们把中间的服务器给省略掉,假设这是个端到端的通信。且为了保证消息的可靠性,我们盲猜它们之间用的是tcp协议进行通信。

聊天软件两端通信

为了发送数据包,两端首先会通过三次握手,建立tcp连接。

一个数据包,从聊天框里发出,消息会从聊天软件所在的用户空间拷贝到内核空间的发送缓冲区(send buffer),数据包就这样顺着传输层、网络层,进入到数据链路层,在这里数据包会经过流控(qdisc),再通过ringbuffer发到物理层的网卡。数据就这样顺着网卡发到了纷繁复杂的网络世界里。这里头数据会经过n多个路由器和交换机之间的跳转,最后到达目的机器的网卡处。

此时目的机器的网卡会通知dma将数据包信息放到ringbuffer中,再触发一个硬中断给cpu,cpu触发软中断让ksoftirqd去ringbuffer收包,于是一个数据包就这样顺着物理层,数据链路层,网络层,传输层,最后从内核空间拷贝到用户空间里的聊天软件里。

网络发包收包全景图

画了那么大一张图,只水了200字做解释,我多少是有些心痛的。

到这里,抛开一些细节,大家大概知道了一个数据包从发送到接收的宏观过程。

可以看到,这上面全是密密麻麻的名词。

整条链路下来,有不少地方可能会发生丢包。

但为了不让大家保持蹲姿太久影响身体健康,我这边只重点讲下几个常见容易发生丢包的场景。

建立连接时丢包

tcp协议会通过三次握手建立连接。大概长下面这样。

tcp三次握手

在服务端,第一次握手之后,会先建立个半连接,然后再发出第二次握手。这时候需要有个地方可以暂存这些半连接。这个地方就叫半连接队列。

如果之后第三次握手来了,半连接就会升级为全连接,然后暂存到另外一个叫全连接队列的地方,坐等程序执行​​accept()​​方法将其取走使用。

半连接队列和全连接队列

是队列就有长度,有长度就有可能会满,如果它们满了,那新来的包就会被丢弃。

可以通过下面的方式查看是否存在这种丢包行为。

# 全连接队列溢出次数# netstat -s | grep overflowed4343 times the listen queue of a socket overflowed# 半连接队列溢出次数# netstat -s | grep -i "syns to listen sockets dropped"109 times the listen queue of a socket overflowed

从现象来看就是连接建立失败。

流量控制丢包

应用层能发网络数据包的软件有那么多,如果所有数据不加控制一股脑冲入到网卡,网卡会吃不消,那怎么办?让数据按一定的规则排个队依次处理,也就是所谓的qdisc(queueing disciplines,排队规则),这也是我们常说的流量控制机制。

排队,得先有个队列,而队列有个长度。

我们可以通过下面的ifconfig命令查看到,里面涉及到的txqueuelen后面的数字1000,其实就是流控队列的长度。

当发送数据过快,流控队列长度txqueuelen又不够大时,就容易出现丢包现象。

qdisc丢包

可以通过下面的ifconfig命令,查看tx下的dropped字段,当它大于0时,则有可能是发生了流控丢包。

# ifconfig eth0eth0: flags=4163<up,broadcast,running,multicast>  mtu 1500inet 172.21.66.69  netmask 255.255.240.0  broadcast 172.21.79.255inet6 fe80::216:3eff:fe25:269f  prefixlen 64  scopeid 0x20<link>ether 00:16:3e:25:26:9f  txqueuelen 1000  (ethernet)rx packets 6962682  bytes 1119047079 (1.0 gib)rx errors 0  dropped 0  overruns 0  frame 0tx packets 9688919  bytes 2072511384 (1.9 gib)tx errors 0  dropped 0 overruns 0  carrier 0  collisions 0

当遇到这种情况时,我们可以尝试修改下流控队列的长度。比如像下面这样将eth0网卡的流控队列长度从1000提升为1500.

# ifconfig eth0 txqueuelen 1500

网卡丢包

网卡和它的驱动导致丢包的场景也比较常见,原因很多,比如网线质量差,接触不良。除此之外,我们来聊几个常见的场景。

ringbuffer过小导致丢包

上面提到,在接收数据时,会将数据暂存到ringbuffer接收缓冲区中,然后等着内核触发软中断慢慢收走。如果这个缓冲区过小,而这时候发送的数据又过快,就有可能发生溢出,此时也会产生丢包。

ringbuffer满了导致丢包

我们可以通过下面的命令去查看是否发生过这样的事情。

# ifconfigeth0:  rx errors 0  dropped 

友情链接