俗讲计算机网络
skaiuijing
引言
有人说,计算机网络就像送快递,不过这样比喻未免太过生硬,让笔者用生活场景带大家理解计算机网络。
生活
想必大家高中都上过晚自习吧,大多时候,晚自习都像这样(老师视角),猴多猴多的猴:

如果距离比较近,大家就直接聊天,如果距离比较远,大家会选择传纸条等方式进行沟通。
有没有办法,建立一个通用的规范呢,让大家的沟通更加顺利呢?
有的,其实计算机网络就是为了解决这些问题的。
让我们跟着主人公小帅的视角,看看他是怎么建立通信规范的吧!
ARP
你叫小帅,是一名刚上高中的学生,在高一年级1144班第10排第1个。
上课第一天,你在窗口发呆时,很快注意到了同班第1排第2个的女生。
当然,这个时候你并不知道她的名字。
那么,怎么办呢?
想了想,你鼓足了勇气,大喊到:“第1排第2个的女生, 我是第10排第1个的男生,我叫小帅,能告诉我你的名字吗?”
很快,一个声音穿了过来,“你好,第10排第1个的小帅同学,我叫小美!”
就这样你知道了小美的名字。
很好,现在记住了,每个人的名字在计算机网络中就叫mac,而高一年级1144班第10排第1个,就叫ip地址。
IP
既然知道了女孩的名字,那么一切都好办了,在上课时,由于两人之间距离比较近,可以写一封信,从小帅直接传递到小美。
IP分片
你与小美的交流就这样开始了,但是由于你写的内容过多,因此一张信封写不下,于是,你想了个好办法,在信的封面又加上字段:
1 | 接收方:小美 |
小美收到后,就知道一共有两封信,要拼接在一起看。
路由匹配
上面的情况在你和小美在同一个班级1144班,可以直接把信送到小美手上,但是如果小美在1164班呢?
首先,ARP肯定是没用的,因为两个班距离比较远,小美根本听不到。
为了解决这个问题,小帅会记录一张表,我们叫它路由表,这张表记录了整个班级的人的ip地址。
然后,班级里面决定选举一位信使,他会把信从一个班送到另一个班,我们叫他网关。
现在,小帅要发给小美消息。
小帅会先查路由表,发现小美的IP不在本班,于是选择默认网关的 IP。
于是,小帅写了一封信:
1 | IP地址;高一年级1164班第1排第2个 |
那么,这封信要送出去,但是很明显,你无法一步到位,直接送到小美那里,你只能送到旁边的网关小李这里。
于是你想了想,又加上了一行:
1 | 接收方:小李 |
然后,你把信递给了小李。
小李看到之后,由于距离原因,他也只能送给旁边的小a、小b、小c。
那么,到底送给谁呢?
很简单,根据ip地址进行匹配。
通常情况下,班级与班级之间都是紧紧挨着的。小美在高一年级1164班,那么肯定不能送到高二年级,而是送到离1164班最近的那位同学手里。
小a在1145班,而小b在1143班,我们都知道,序号越接近距离越近,那么,肯定送给小a,于是,这封信变成了下面这样:
1 | 接收方:小a |
同理,小a看了看,发现小g在1146班,而周围其他网关同学在1145等班级班,于是,小a把信给小g。
就这样,信到了1164班第十排第三个的网关小黄同学手里,而他距离小美只有一段距离,在这里,小黄进行查表后发现,小美就在本班,于是他发送ARP广播获得小美的mac,然后把信补充完整,再把信给到小美手上。
在计算机网络中,就是路由。
现在,我们已经跑通了整个流程,但是,这仅仅是开始。
校验和
在上面,我们假设信的内容是完整的,中间过程没有被篡改,但是,实际情况肯定不能这样,那么,如何检验信是否完整呢?
小帅很聪明,马上,他想到了用特定值表示每一个汉字,然后把所有字的值加起来,最后就能得到一个数,把这个叫做校验和的数写在封面。
1 | 接收方:小李 |
当小美拿到信后,把信上所有字加起来,然后进行检验,就能判断这封信的内容有没有被篡改过。
当然,其实这里小帅用到了一些小学二年级学过的抽象代数的内容(毕竟大家都是在这个时候学数学运算)。
在这里,操作步骤并不是简单的相加,还涉及一些步骤,但是考虑观众基础,笔者这里还是讲简单点。
ICMP
有的时候小美不在座位上,一封信写出去半天没有回应,有什么好办法呢?
小帅想到了,进行通信前,先写信测试一下:
1 | 接收方:小李 |
如果小美回复:
1 | 接收方:小黄 |
那么,小帅就知道小美是可以收到消息的,就可以放心沟通了。
这就是ping命令。
UDP
刚开始,小帅写完信后,就立刻送出去了,然后想到什么写什么,小美也这样做,很快,一些问题暴露出来了。
由于邪恶的教导主任和班主任经常在班与班之间或者班内巡逻,你们的信有不少被他收缴起来了,还有不少信在传递过程中就出现了丢失,甚至,经常有人冒充小美,给你写信。
你们的通信非常不顺利。
怎么办呢?
TCP
确认
小帅很快想到了办法,保证连接的一个很直接的思想就是进行一个确认:
小帅规定,在双方进行通信的过程,接收方收到数据后,要向发生方发生一个确认的信封:
小帅:发了一个数据报,你收到了吗?
小美:我收到了
但是,这样还不够,你们担心数据混乱问题,又设置了一些方法:
三次握手
你们各自设置了一个初始序列号ISS,当写信时,双方交换序列号。
在信封上,你们加上了两个字段,一个是seq,另一个是ack,seq表示期望发送的序列号,ack是期望接收的序列号
于是,信变成了这样:
1 | seq: 期望发送的序列号 |
这样做有什么用呢?
建立连接
你先初始化随机化iss的值,把这个值写入seq,然后发出去。
1 | seq: 小帅的ISS |
小美看见这封信之后,也回复:
1 | seq: 小美的ISS |
小帅再收到这封信时,更新seq和ack,并且送信确认:
1 | seq: 小帅的ISS + 1 |
这样做的好处是什么呢?你们可以使用序列号表示接下来的信的字的个数,如果你们发现序列号对不上,那么就代表可能出现了丢失信封的情况。
那么,为什么是三次握手呢?
其实很简单,就算确保双方都能互相确认:
小帅->小美,小美收到信后,知道了小帅可以向他发送消息,但是小帅还不知道这一点。
小美->小帅,小帅知道B可以向他发送消息,但是小美还不知道。
小帅->小美,小美知道小帅知道小美可以向他发送消息。
好了,现在双方都知道了可以互相发送消息了。
定时器与重传
由于经常发生丢包事件,每当小帅写了一封信半天没有回应后,都会选择重新写一封寄出去。
为了优化流程,小帅决定计时。
当每一封信发出去时,如果五分钟之内,还没有收到小美的确认信,那么小帅就决定重新发送。
但是,对于每一份信都用五分钟这个标准,肯定是不适用的。
RTT估计
如果仅仅是拿上一次一封信的往返时间来判断这一次大概要花多久,多少有点不严谨,如果全部取平均数,又忽略了现实的变化。
于是小帅设计了一个公式,用来估算一封信的往返时间,也就是这封信发出,再到收到小美的ACK:
1 | RTT = a*历史数据 + (1 - a)最近一次的数据 |
其中a一般是7/8,也就是7/8是历史数据,1/8是这一次的时间。
RTO
那么我们可以在这个基础上设置最大容忍时间,只要超过了这个时间,我们就认为这封信被老师收缴或其他原因导致丢失了,需要重新写。
拥塞控制算法
当一封信丢失时,极大概率是因为小美收到太多信了,处理不过来,在这个时候,如果继续写信,会加大小美的负担。
因此,丢包是一个非常重要的信号,每当发现出现较多丢包情况时,小帅会选择降低写信频率,从而减少发送的数据量。
如果信的发送一直非常顺利,那么小帅就会加快发送信件的频率。
Linux内核BBR算法
但是,还有一种可能,那就是这封信被教导主任收缴了,其实小美本身并没有收到过多信件,在这种情况下,其实也是可以继续写信的,如果教导主任收缴信过多,那么再降低发送频率也不迟。
小帅又想到了一种算法,可以通过一封信的往返时间的变化来判断小美是否繁忙,而不必依赖丢包作为信号。
保活机制
有时候,小帅和小美双方长时间没有沟通,但是没有哪一方提出要结束通信,这个时候,怎么判断双方都在线呢?
小帅想到了,可以每隔一段时间,向小美发送一份信件,如果小美回复了,那么说明她在线,如果不回复,说明通信结束了。
结束连接
其实结束双方通信与建立连接是差不多的,这里就不过多赘述了。
攻击
你和小美的交流一直很顺利,但是这引起了黄毛同学的不满,于是他想到了一些方法,来破坏你们之间的交流。
ARP攻击
小黄同学欺骗了班上的网关同学,称高一年级1164班第1排第2个的女生就是自己,于是,网关把信改成了这样:
1 | 接收方:小黄 |
当网关拿到信以后,会直接把信交给小黄,现在小黄可以:
1.修改信的内容再把它转发给小美
2.直接丢弃
SYN攻击
由于小美每天都会收到大量信件,为了标识每一个连接,她会用很多盒子(内存)装来自不同人的信。
在其中,第一次新的连接发送过来的信被称之为SYN,小美看见这种信后,就知道有一位新同学想建立通信,于是,她会用一个盒子封装必要的信息,比如这个连接的双方是谁等等。
小黄同学发现这一点后,于是构造了非常多的SYN信件发送给小美,很快,小美由于桌上信封太多,根本来不及处理小帅的消息了。
TCP重置攻击
小黄通过伪造信件,向小帅和小美发送伪造的消息,告诉她们立即断开连接,小帅看见信后,以为小美有急事,于是马上断开了连接,而小美也以为小帅有急事,双方就这样断开了连接。