剖析一下“计算机网络”的世界

10 篇文章 0 订阅
5 篇文章 0 订阅

参考资料:
公众号:低并发编程
https://mp.weixin.qq.com/s/jiPMUk6zUdOY6eKxAjNDbQ
https://mp.weixin.qq.com/s/Uf42QEL6WUSHOwJ403FwOA
书籍:《TCP/IP详解卷一》、《Unix网络编程卷1:套接字联网API》

一、数据包是怎么从主机A发到主机B的

1.1 物理层

主机A与主机B通信,我们用一根网线就能实现目的,可是随着主机数量增加,给每个主机多开几个网口显然是不现实的。

于是一种中间设备----集线器就被发明出来了,所有网线都插到这个设备上,交由集线器进行转发。然而集线器的作用仅仅只是将电信号广播到所有设备,不会做任何的数据处理。

此时会出现一个问题,如果有五台机器ABCDE,那么由A发送的数据包到底是给谁的呢?于是我们需要给所有设备都起个独一无二的名字作为标识,即MAC地址

如A的MAC地址为11-11-11-11-11-11,B的MAX地址为22-22-22-22-22-22,那么可以给发送的数据包中加一个包头,如下图:
在这里插入图片描述
那么其他主机在收到数据包之后,只要验证包头中的目的Mac和自身Mac是否匹配,从而决定接收或者丢弃。

显然,集线器的作用很简单,我们需要进一步改进。

1.2 链路层

由于集线器会将数据包广播给所有设备,这完全没有必要,于是我们一种更加智能的设备—交换机

交换机为了解决上述问题,其内部有一张MAC地址表,记录每一个MAC地址及其连接的端口,如下图:
在这里插入图片描述
此时A要给B发送一个数据包,构造的数据包如下图:
在这里插入图片描述
当该数据包到达交换机时,交换机会审查所维护的表,如果表上存在目的主机B的MAC,直接将数据包从对应的端口转发给B即可。

那么交换机的这张表是怎么建立起来的呢?
若表为空时,此时给主机B发送的数据从进入交换机的端口1,于是交换机就得到了第一条映射数据:

MAC:11-11-11-11-11-11
端口:1

此时交换机在表中是找不到目的MAC地址的,于是只能广播给所有的机器,主机B在收到给自己的包后,会做出响应,对应数据会从交换机的端口2进入,于是交换机可以更新第二条映射数据:

MAC:22-22-22-22-22-22
端口:2

经过多次通信后,交换机中的MAC地址表也完善了起来。
而随着机器数量增加,交换机的端口数量自然就不够用了,此时就需要将多个交换机连接起来即可,当然,不同交换机之间将更新对方所记录的所有MAC地址记录哈。

这种方法当然还是不能从根本上解决问题,机器的数量达到数十万以上时,我们该怎么解决呢?

1.3 网络层

交换机所存在的问题就是随着机器数量太多时,所维护的Mac地址表将变得很庞大,于是我们发明了一种新的设备—路由器

路由器的功能就是帮助交换机做一次转发,帮助交换机摆脱庞大的MAC地址表,此外,路由器的每一个端口都有独立的MAC地址。
在这里插入图片描述
此时,交换机1和交换机2只要多出一条MAC地址到路由器就能成功将数据包转交给路由器了。

好了,现在又引出一个问题,主机A是怎么将发送到主机DEF的数据包发送到路由器的呢?我们需要一些机器的标识来进行区分,显然,靠MAC地址是做不到的,因为这个在设备出厂的时候就固定了,于是,一个新的标识编号被发明了—IP地址

这里我们以ipv4为例,给每一台机器一个32位的编号,十进制表示为:

192.168.0.1

此时每一台设备就都有了一个可以更改的IP地址,如下图
在这里插入图片描述仅需要将IP地址为192.168.0开头的全部都发给路由器,让路由器决定后续的转发,此时路由器的IP地址就是我们熟知的默认网关了,这个地址可以自己设置。那路由器后续又是怎么转发数据包的呢?

我们重新绘制拓扑图如下:
在这里插入图片描述
此时,IP地址主要是在跨域通信时发挥作用,而在主机ABC的互相通信中,由于在同一个交换机下,查MAC地址表就够用啦!

那我们主要来分析主机A发送给主机E的过程,显然我们需要将数据包发送给路由器,路由器再将数据发送给主机E,数据包结构如下:
在这里插入图片描述
那么A发给E的数据包怎么知道要通过路由器呢?这就涉及到子网的概念了。在同一个子网下的数据包交给交换机就能解决,不在同一子网下的数据包就交给路由器处理。

讲到这里,我们还需要对子网概念进一步进行解释,这里就需要子网掩码的概念了,我们将不同的IP地址与子网掩码做一个与运算,若结果相同则处于一个子网,反之则不在。举例如下:

子网掩码:255.255.255.0
主机A:192.168.1.1 & 255.255.255.0 = 192.168.1.0
主机D:192.168.0.1 & 255.255.255.0 = 192.168.0.0

显然,主机A和主机B并不在同一子网下面。

现在我们知道了要将不同子网的数据包交给路由器,那路由器又是怎么知道我们要发给谁的呢?我们想到了交换机中的MAC地址表,同样的路由器也有一个路由表

路由表中的内容比起MAC地址表要更加复杂,我们先给出一个例子,如图:
在这里插入图片描述
其中**/24**表示子网掩码的长度为24位,即255.255.255.0。此时位于192.168.0子网下的数据都从端口1进行发送,位于192.168.1子网下的数据都从端口2进行发送,那下一跳中的内容是做什么的呢?

不难理解,一个数据包一般要经过若干个路由器的转发才能到达目的主机,从源IP所在的路由器即源路由器到目的IP所在的路由器即目的路由器,中间必然就存在路径选择的问题,如图:
在这里插入图片描述
下一跳路由器的选择其实就是一个最优路径选择的问题,目前常用的算法有Dijkstra算法和LS算法,这里有兴趣的同学可以进一步去研究哈!

那么当我们找到了目的IP所在的路由器时,最后就只要找到目的主机就行了呀!接下来就需要MAC地址的帮忙了!那么我们怎么找到目的IP对应的MAC地址呢,毕竟一个子网中还有辣么多主机呢?

这里就需要ARP协议的帮忙啦!这里可能有同学有疑问?前面讲的数据包里不都有MAC地址吗,证据如下:
在这里插入图片描述
emmm最开始的时候,我们是不知道目的MAC地址的,这是我们假设的情况,我们知道的仅仅是目的IP,而MAC地址的值则需要ARP协议的帮忙了。

也就是说,ARP协议的功能就是帮助我们找到IP地址对应的MAC地址,它用的方法是这样的:主机A为了知道主机B的MAC地址,主机A会广播一条ARP请求,主机B在收到请求后,确认对方找的就是自己,此时会给主机A一个响应,然后A就会更新一个叫ARP表的东东,其内容就是一条IP地址和对应的MAC地址,而随着通信的频繁,在该子网中的ARP表内容就会完整起来了。

此时,主机A发送的数据包就到达了主机B啦!

二、主机B收到的数据怎么到达进程的

2.1 传输层

2.1.1 丢包和乱序

在前面三层中已经能够将数据成功送达目的主机了,那么后面的问题就是将数据包准确送到目标进程了。

主机上工作的进程这么多,我们就要对其进行区分,于是使用一个叫做端口号的编号,比如我们常用的http进程绑定的80端口。此时我们又需要给数据包加上一个头,它至少要包含源端口号和目的端口号,此时,倘若再加上包长度和校验值的话,一个叫UDP协议的东西就诞生了呀!

此时,使用UDP协议已经能达到进程之间通信的目的啦!然而,网络往往比咱们想的要复杂,在一个不可靠的网络上传输数据包,很可能到不了目的主机,更别说到目的进程了。

那我们就需要增加一些手段来解决数据包丢失的问题,首先我们要让发送方知道数据包丢失,然后再重传数据包。

最开始的方法是这样的,主机A每发送一个包,都要等待主机B的ACK确认包,如果在一定时间内没有收到确认,则重传,这就是停止等待协议

显然,这种方法效率太低了,于是我们采取一种流水线式的方法,发送多个包就回复多个ACK确认,此时又会带来新的问题,当发送数据包由于网络延时到达目的主机时出现了乱序问题,于是我们又在主机A发送的数据包中增加一个seq序号,主机B响应的ACK包上增加一个ack确认号(该确认号为收到的最后一个数据包的序号seq+1,表示seq之前的包都收到了,即累计确认)。

2.1.2 流量控制

前面的方法已经可以解决丢包和乱序问题,此时我们就可以随心所欲的发送数据包了吗?答案是可以发,但未必随心所欲。

试想一下,如果主机A发送速度巨快无比,而主机B收包的速度又无比的慢。没错,试想一下节假日堵在高速的我们。

遇到这种问题时,应该让主机A知道主机B自己的接收能力,主机A再控制下发送速度就OK,这个接收能力的大小怎么表示呢,我们用到了一个窗口—滑动窗口(看到这里,大伙是不是开始兴奋啦!)。

于是乎,A和B每次通信都会加上一个表示窗口大小的值,代表自己的接收能力,那么问题来了,对方主机怎么根据这个窗口来控制自己的发送速度呢?

举个例子,当主机B传给主机A的窗口大小为4,即主机B目前还能接收4个数据包,如图:
在这里插入图片描述
如图,绿色区域就是根据主机B目前的窗口大小所控制的数据包个数,主机A将从数据包6开始一直发包直到数据包9发送完毕,在此期间主机B也会发送确认包给A,当主机A收到数据包6的确认时,窗口的右边界将向右移动,主机A将继续向B发送新的数据包。

当然,若主机B发送了一个新的窗口值8,则主机A可发送数据包窗口的大小将变为8;反之,如果收到的窗口值变小,则主机A暂时不会改变窗口大小,也不会将窗口右边界往左回移,而是等着B发送的确认包,将窗口左边界向右移动,直到窗口大小变为新的窗口值为止。

2.1.3 拥塞控制

流量控制是应对对端主机接收能力的变化的策略,而拥塞控制则是应对网络环境变化的策略。

拥塞控制也是通过设置类似窗口大小的方法来应对的,不同于流量控制中主机B的主动通知窗口大小,拥塞控制则是主机A一次次的去探测网络环境好坏,从而确定拥塞窗口(cwnd)的大小。结合上一节流量控制中的接收端窗口大小(awnd),则发送端实际可以用的窗口(W)为两者中的较小值:

W = min(cwnd, awnd)

那么拥塞检测的方法有哪些呢?接下来我们将讨论一些经典的拥塞控制算法。

慢启动

当一个新的连接建立时,需要执行慢启动算法,简单来说,发送方先发送一个单位的数据包,在收到ACK响应之后,窗口cwnd大小变为2,以此类推,在没有丢包且每个数据包都有对应的ACK时,在k轮之后cwnd的值变为2k

显然发送端实际可用的窗口不可能一直呈指数上升,当窗口达到某个值时,大量的数据包导致网络堵塞甚至瘫痪,这时cwnd的值将减小至原值的一半,而后将转到下一个阶段—拥塞避免
该图来自《TCP/IP详解卷一  P733》

拥塞避免

在慢启动阶段,拥塞窗口cwnd快速增长至阈值(ssthresh),后续将改变策略,采用一种随时间线性增长的方式,这里具体的公式就不再给出了哈,咱直接上《TCP/IP详解卷1》中的图!
在这里插入图片描述

快重传和快恢复

前面两种算法都是在没有发生丢包的情况下,倘若发生丢包或延时,显然重新由慢启动的最初状态开始是不太合理的。

先上结论,当发送端接收到三个重复的ACK值时,发送端就会判断数据包丢失,此时进行快重传,快重传会将阈值ssthresh更新为原值的一半(具体的我就不写出来了哈),之后再重新进入拥塞避免阶段。

快恢复则是对其进一步补充,再接收到一个重复ACK值时,cwnd值将增加1个单位,而当接收到新的ACK值时,说明前面丢失的数据全部接收完毕,可以恢复到最开始的ssthresh值了。

2.1.4 三次握手和四次挥手

关于握手和挥手的有关细节,在我主页的其他文章也给出了分析哈,链接在这:TCP三次握手、四次挥手以及TIME_WAIT详解

2.2 小节

文章写到这里,我们已经悄无声息的将TCP协议的整个流程大致描述了一遍,过程当然还是有很多瑕疵哈,望各位同学不吝赐教,指出文章中存在的问题,感谢感谢!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿杰的小鱼塘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值