ss -s
表面上我是个技术博主。
但没想到今天成了个情感博主。
我是没想到有一天,我会通过技术知识,来挽救粉丝即将破碎的感情。
掏心窝子的说。这件事情多少是沾点功德无量了。
事情是这样的。
最近就有个读者加了我的绿皮聊天软件,女生,头像挺好看的,就在我以为她要我拉她进群发成人专升本广告的时候。
画风突然不对劲。
她说她男朋友也是个程序员,异地恋,也关注了我,天天研究什么TCP,UDP网络。一研究就是一晚上,一晚上都不回她消息的那种。
话里有话,懂。
不出意外的出了意外,她发出了灵魂拷问
"你们程序员真的有那么忙吗?忙到连消息都不知道回。"
没想到上来就是一记直拳。
但是,这一拳,我接住了。
我很想告诉她"分了吧,下一题"。
但我不能。因为这样我就伤害了我的读者兄弟。
沉默了一下。
单核cpu都快转冒烟了,才颤颤巍巍在九宫格键盘上发出消息。
再回慢一点,我就感觉,我要对不起我这全日制本科学历了。
"其实,他已经回了你消息了,但你知道吗?网络是会丢包的。"
"我来帮他解释下,这个话题就要从数据包的发送流程聊起"
数据包的发送流程
首先,我们两个手机的绿皮聊天软件客户端,要通信,中间会通过它们家服务器。大概长这样。
但为了简化模型,我们把中间的服务器给省略掉,假设这是个端到端的通信。且为了保证消息的可靠性,我们盲猜它们之间用的是TCP协议进行通信。
为了发送数据包,两端首先会通过三次握手,建立TCP连接。
一个数据包,从聊天框里发出,消息会从聊天软件所在的用户空间拷贝到内核空间的发送缓冲区(send buffer),数据包就这样顺着传输层、网络层,进入到数据链路层,在这里数据包会经过流控(qdisc),再通过RingBuffer发到物理层的网卡。数据就这样顺着网卡发到了纷繁复杂的网络世界里。这里头数据会经过n多个路由器和交换机之间的跳转,最后到达目的机器的网卡处。
此时目的机器的网卡会通知DMA将数据包信息放到RingBuffer
中,再触发一个硬中断给CPU
,CPU
触发软中断让ksoftirqd
去RingBuffer
收包,于是一个数据包就这样顺着物理层,数据链路层,网络层,传输层,最后从内核空间拷贝到用户空间里的聊天软件里。
画了那么大一张图,只水了200字做解释,我多少是有些心痛的。
到这里,抛开一些细节,大家大概知道了一个数据包从发送到接收的宏观过程。
可以看到,这上面全是密密麻麻的名词。
整条链路下来,有不少地方可能会发生丢包。
但为了不让大家保持蹲姿太久影响身体健康,我这边只重点讲下几个常见容易发生丢包的场景。
建立连接时丢包
TCP协议会通过三次握手建立连接。大概长下面这样。
在服务端,第一次握手之后,会先建立个半连接,然后再发出第二次握手。这时候需要有个地方可以暂存这些半连接。这个地方就叫半连接队列。
如果之后第三次握手来了,半连接就会升级为全连接,然后暂存到另外一个叫全连接队列的地方,坐等程序执行accept()
方法将其取走使用。
是队列就有长度,有长度就有可能会满,如果它们满了,那新来的包就会被丢弃。
可以通过下面的方式查看是否存在这种丢包行为。
# 全连接队列溢出次数
# netstat -s | grep overflowed
4343 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
从现象来看就是连接建立失败。