1.❤️❤️前言~🥳🎉🎉🎉
Hello, Hello~ 亲爱的朋友们👋👋,这里是E绵绵呀✍️✍️。
如果你喜欢这篇文章,请别吝啬你的点赞❤️❤️和收藏📖📖。如果你对我的内容感兴趣,记得关注我👀👀以便不错过每一篇精彩。
当然,如果在阅读中发现任何问题或疑问,我非常欢迎你在评论区留言指正🗨️🗨️。让我们共同努力,一起进步!
加油,一起CHIN UP!💪💪
🔗个人主页:E绵绵的博客
📚所属专栏:1. JAVA知识点专栏
深入探索JAVA的核心概念与技术细节
2.JAVA题目练习
实战演练,巩固JAVA编程技能
3.c语言知识点专栏
揭示c语言的底层逻辑与高级特性
4.c语言题目练习
挑战自我,提升c语言编程能力
5.Mysql数据库专栏
了解Mysql知识点,提升数据库管理能力
6.html5知识点专栏
学习前端知识,更好的运用它
7. css3知识点专栏
在学习html5的基础上更加熟练运用前端
8.JavaScript专栏
在学习html5和css3的基础上使我们的前端使用更高级、
9.JavaEE专栏
学习更高阶的Java知识,让你做出网站
📘 持续更新中,敬请期待❤️❤️
2.应用层协议
自定义协议
在应用层这里, 很多时候, 都是程序员"自定义"应用层协议的,(当然,也是有一些现成的应用层协议,咋们之后讲)
咱们这里的自定义协议,很简单就能去设定一个,设定规则如下:
1.发送数据:按照我们自己设定的方式去得到信息内容 再正常去传输它接受数据:按照自己设定的方式处理 正常接受的数据
2.约定好信息按照什么格式来组织
信息的格式
那么信息可以按照什么格式呢?
1.基于行文本的方式传输(自定义格式)
“1234|180E40N”或者“1234,180E40N”等都可以为自定义格式,我们在这字符串里面用什么样的分隔符都是可以灵活进行选择的,只要确保客户端和服务器, 使用同一套规则进行通信即可。
自定义格式的可维护性比较差,当属性多了的时候,放眼看上去一片混乱
2.基于 xml 的方式
它是一种经典的数据组织格式,下面是它的格式:<request> <userld>1234</userld> <position>180E40N</position> </request>
这里我们看到它跟html格式一样,对于html 可以理解成一种特殊的 xml,xml 也是成对的标签来组织的,xml 中,内部标签的名字是啥, 标签如何嵌套都是你可以自定义的.
这里有一个缺点:在网络传输中,它会消耗额外的带宽 (需要把 key 也进行传输)
3. 基于json的方式传输
这是当前最流行,最广泛使用的方式,当前在网络通信的时候经常用到 json 格式尤其是后面学到 web 开发,json 会用的非常多,而且可读性非常好,比 xml 更简洁
格式如下:
{ userld: 1234, position: "180E40N" }
用 {}作为边界.{}里面是键值对
键值对之间使用,分割 键和值之间使用:分割.json虽然比xml更简洁,但同样在网络传输中,它也会消耗额外的带宽
4.基于protobuffer(pb)的方式传输
前面几种都是 文本格式,肉眼能看懂,pb 则是 二进制 格式了,肉眼看不懂,也没法手搓出一个例子出来
它针对要传输的数据进一步的整理和压缩了虽然可读性不好,能够把空间最充分的利用,最节省网络带宽,效率也最高,对于传输效率要求高的场景 推荐用这个
现成的协议——DNS协议
其中最典型的是http协议,之后会详细讲解,这里我们讲另外的一个现成协议——DNS协议
在说DNS协议之前,我们先说DNS,DNS又叫域名解析系统,由DNS协议和DNS服务器组成
这里出现了个域名的概念:域名是什么呢?它是由于ip不易被人理解,所以我们就把ip变为易理解的域名。
一个典型的域名由多个部分组成,从右到左分别是最顶级域名、二级域名,以及可选的子域名。
例如 https://kimi.moonshot.cn/chat/cvi2du6s1rh9tkh0jkpg 是一个网址,其中域名是
kimi.moonshot.cn
具体来说:
顶级域名:
.cn
(中国国家顶级域名)二级域名:
moonshot
(主域名)子域名:
kimi
(子域名,用于进一步区分不同的服务或站点)
那么DNS作用是什么呢?
在域名解析过程中,客户端(如用户的计算机)会向DNS服务器发送一个基于DNS协议的请求,请求中包含需要解析的域名。DNS服务器收到请求后,会根据其记录查找对应的IP地址,并按照DNS协议的格式返回给客户端。
上述是DNS的作用,由DNS协议和DNS服务器一起完成,那么对于其内部的组成DNS协议和DNS服务器的作用分别是什么?
DNS协议:是域名解析服务所遵循的规则和标准,定义了如何进行域名与IP地址的转换,以及如何在不同的网络设备之间传递解析请求和响应。
DNS服务器:是实际存储域名与IP地址映射关系的计算机或设备,它依据DNS协议来响应客户端的解析请求。
因此,域名解析系统是DNS协议和DNS服务器共同构成的一个完整体系,缺一不可。
那么这里有一个问题:全世界,无时不刻都有很多设备需要进行 DNS 的请求, 这一组 DNS 服务器,能抗住这么高的请求量嘛??
肯定是不能的,所以我们通过两个方法去解决:
1.开源
搭建DNS系统的大佬们,就开始号召各个网络运营商,你们都可以自己搭建一组"DNS 镜像服务器",镜像服务器的数据,都从他们这边来实时同步,此时用户就会优先访问离自己最近的镜像服务器,这样就减少源服务器的请求量。
2.节流,让请求量变少,让每个上网的设备,搞本地缓存
我的电脑 1min 之内要访问 10 次 www.sogou.com,但只需要第一次请求 DNS 即可,把请求得到的结果ip保存到本地,后面9 次请求都使用第一次的结果即可,这样就减少了请求量。(域名的变换,没有那么频繁)
这就是DNS协议的具体讲解,对于应用层的其他具体现成协议我们会在之后单开一个文章讲解。
3.传输层协议
传输层协议分为udp和tcp,之前已经讲过它们的区别,这里深入讲下它们的结构,由于tcp比udp复杂的多,我们先讲udp。
UDP
UDP的传输形式是基于数据报的,UDP数据报分为UDP 报头和UDP 载荷(完整的应用层数据报)
下面我们根据这udp数据报的结构去深入探究udp:
UDP首部有8个字节,由4个字段构成,每个字段都是两个字节,
由于传输层跟端口密切相关,所以这里理应存在目的端口号和源端口号,都为两个字节。
UDP长度描述了整个UDP数据报占多少个字节,由于udp长度存储单位为两个字节,预估整个udp数据报大小为64kb(这里我们也可以看出UDP的一个弊端,在当今社会,64kb实在太小了,而由于一些原因,UDP所携带的数据大小无法改变,如果有大数据要通过udp传输就需要拆包和合包,tcp就没这个烦恼,无大小限制)
前提: 网络传输中,由于一些外部干扰,就可能会出现数据传输出错的情况,
光信号/电信号磁场,电场, 高能离子.. 某个地方本来是传输低电平, 在干扰下就成了高电平了,比特翻转,所以数据发生变化。
因此,就需要有办法,能够识别出出错的数据.校验和就是这样的一种检查手段校验和:检测UDP数据报在传输中是否发生改变,改变了则传输过来的数据包则丢弃且重传。
校验和其实本质上是一个字符串,体积比原始的数据更小, 通过原始的数据用特定算法生成。原始数据相同,得到的校验和就一定相同.反之,校验和相同,原始数据大概率相同 (理论上会存在不同的情况, 实际的概率非常低,可以忽略不计),校验和不同则原始数据一定不同。
如何基于校验和来完成数据校验呢?
1.发送方,把要发送的数据整理好(称为 data1),通过一定的算法,计算出校验和 checksum1
2.发送方把data1 和 checksum1 整合为数据报一起通过网络发送出去.
3.接收方收到数据, 收到的数据称为 data2(数据可能和 data1 就不一样了), 接收方再根据 data2 重新计算校验和 (按照相同的算法),得到 checksum2
4.对比 checksum1 和 checksum2 是否相同. 如果不同,则认为 data2 和 data1 一定不相同.
如果 checksum1 和 checksum2 相同,则认为 data1 和 data2 是相同的(理论上存在不同的可能性,概率比较低,工程上忽略不计)
计算校验和,有很多种算法:
此处 UDP 中使用的是 CRC 算法(循环冗余算法)
把当前要计算校验和的数据,每个字节,都进行累加,把结果保存到这个两个字节的变量中,如果中间某个数据, 出现传输错误, 第二次计算的校验和就会和第一次不同
CRC 这个算法其实不是特别的靠谱,导致两个不同的数据,得到相同的 crc 校验和的概率比较大.(前一个字节恰好少1,后一个字节恰好多1),这里的概率大是相较于其他算法的概率比的,在正常应用中还是可以忽略不计这个误差的。
除了该算法,还有md5/sha1 算法 (这里我们就只介绍 md5)
这里有一系列的公式,来完成 md5 的计算,(咱们不需要考虑公式是啥样的,是一个数学问题),但是咱们需要知道 md5 的特点:
1.定长:无论你原始数据多长, 计算得到的校验和都是固定长度,校验和本身就不应该很长,要不然不方便网络传输
2.分散:给定两个原始数据,哪怕绝大部分内容都一样,只要其中1个字节不同,得到的校验和值都会差异很大.3.不可逆:给你一个原始数据,计算 md5非常容易.给你 md5还原出原始数据, 计算量非常庞大,以至于超出了现有计算机的算力极限,理论上是不可行的(所以md5 也可以应用在一些密码学场景中)
md5 在工作中非常常用,大家一定要熟悉这几个基本特点.
相比于 UDP 来说,TCP 在更多的情况下是具有优势的,很多时候我们都是优先考虑使用 TCP,它也更复杂,接下来我们来讲解一下tcp。
TCP
我们同样根据这tcp数据报的结构去深入探究tcp:
其数据部分称为载荷,数据部分之外的称为报头。由于TCP是可靠传输,所以报头部分相比udp会格外复杂。
16位源/目的端口号:跟udp一样
32位序号/32位确认号:后面会详细讲到
4位TCP报头长度:表示该TCP报头的长度,其长度是可变的,不跟udp一样(长度为20—60字节)
保留(6位):未来某天TCP需要扩展一些新功能,就可以使用这个保留位来表示,暂时还没有用到
6位标志位:之后内容会讲到16位窗口大小:后面会详细讲到
16位校验和:跟udp的校验和一摸一样,这里不再叙述了
16位紧急指针:标识哪部分数据是紧急数据(用的不多,不会详细说明)
选项:可理解为是可选项,可以选择加还是不加(这块可有可无就对TCP的报头产生影响),如果选项完全没有,tcp报头长度就是20个字节,如果选项拉满,tcp报头最长是60个字节。报头最大长度是60,去掉固定的20,剩下选项部分最多40个字节。
TCP的10个核心机制
在刚才对tcp的数据报研究中,我们发现了很多udp不存在的结构,而这些结构都是跟我们TCP的核心机制有关,核心机制总共有10个,下面我们一个一个讲解这些机制并附带说明这些多出来的结构。
TCP有一个最核心的特性:可靠传输, 它是 TCP 最最核心的特性
可靠传输,不是说,发送方把数据能够 100% 的传输给接收方(要求太高了)
而是发送方发出去数据之后,能够知道接收方是否收到数据,一旦发现对方没收到,就可以通过一系列的手段来补救重新去发送。
确认应答机制
确认应答机制是保证TCP可靠传输的一个核心机制。
确认应答机制:发送方把数据发给接收方之后,接收方收到数据就会给发送方返回一个应答报文(ack报文),发送方如果收到这个应答报文了,就知道自己的数据是否发送成功了(应答报文是什么我下面会解释)
单看这一幅图我们知道一般数据在TCP的传输通常都是一个接着一个,只有确认了一个数据传输成功才会传送下一个数据。
但这里还有个疑问:为什么会有数字的出现?
其实这数字指的就是序号和确认序号。
在网络传输过程中,会出现一种情况,叫做"后发先至",也就是后发的消息反而先到,先发的消息反而后到,所以我们就不能根据到达顺序去将ack报文和数据报进行一一对应。
所以我们就引入序号和确认序号来将ack报文和数据报进行一一对应。
(对于这种数据一个个传输的其实并不存在后发先至,因为一次传输它就一个数据,后发先至情况一般都出现在滑动窗口机制中,那个机制是一次传输很多数据,之后会详细讲到)
注意:后发先至是网络通信中客观存在的,改变不了。
我们引入一个序号和确认序号。只要能够对传输数据进行编号,并且让应答报文的编号和发送数据的编号,能够一一对应,即使出现后发先至,也不影响对于传输意思的理解
在上述的32位序号和32位确认序号,此时32位序号就是发送数据的序号,32位确认序号就是给应答报文用到,这样的数据就可以根据确认序号区分出要应答哪个上面的数据了。
假设一个TCP 数据包里一共有 1000 个字节的载荷数据.其中第一个字节的序号是 1,就在 TCP数据报的序号字段中写1.
由于一共是 1000 个字节,此时最后一个字节的序号自然就是1000 了.但是 1000 这样的数据并不需要在 TCP 报头中记录。因为剩下其他字节的序号,都可以依次的推出,所以只需要写第一个(虽然 tcp 序号是连续递增,但不一定是从 0 或者1开始的,具体从哪里开始,为啥这么设定,后面"连接管理"再详细解释)
确认序号设定也非常有特点,确认序号取值就是要在应答的数据的最后一个字节的序号再 +1,所以依照上面的假设,在应答报文中, 就会在确认序号字段中填写 1001,这样就表示1001 之前的数据, 都被 B 收到了。
那么说了这么久,如何区分应答报文(ACK报文)和正常的数据报呢?
这就到了我们所说的标志位中的ACK:
这一位为 1,表示当前数据包是一个应答报文.此时数据包中的"确认序号字段"就生效.
这一位为 0,表示当前数据包是一个普通报文.此时数据包中的"序号字段"就生效.此外应答报文中没有载荷,正常数据报里有。
所以我们通过特殊的 ack 数据包里面携带的"确认序号"就能告诉发送方,哪些数据已经被确认收到了。此时发送方就心中有数了,就知道了自己刚发的数据是到了还是没到,这就是可靠传输。
TCP 的初心是为了实现可靠传输 ,达成可靠传输的最核心的机制就是确认应答机制
所以这里有个面试题:
TCP 是如何保证可靠传输的??
正确答案:通过确认应答为核心,借助其他机制辅助最终完成可靠传输
错误答案:三次握手/四次挥手保证了可靠传输. (这个是错误的,它们只是为可靠运输提供了基础和保障,算是辅助机制,不是核心机制)
超时重传机制
TCP最核心的功能就是可靠传输,可靠传输之所以能达成,主要是依靠"确认应答"机制,这里是通过应答报文来通知发送方,我已经收到请求,这是一切顺利的情况,如果不顺利呢?假如出现"丢包"呢??
丢包是指数据在传输过程中,被丢弃,无法到达对端,也是客观存在的随机事件。
那为什么会出现丢包,是因为在网络传输过程中,里面错综复杂,A给B传输数据,会经过多个路由器和交换机,这些路由器交换机又不止是给A和B提供数据传输服务,整个网络是非常繁忙的。若其中一个路由器/交换机过于繁忙,需要处理的数据量超过极限,那么就会把多出的部分直接丢弃,这就丢包了。
那么就引入了超时重传机制,就是用来应对网络出现丢包的情况,针对确认应答机制,进行补充。正常情况下,TCP就是通过确认应答来知道数据是否被对端收到了。假如A给B传输数据,若过程中出现丢包了,那么B不会收到A发来的数据,此时B也不可能给出任何应答。A就可以根据"是否收到了ACK"来区分是否出现丢包。
当A从发送数据之后,到正常收到ACK,在这中间肯定也需要一定的时间,A就会进行等待,如果等待时间超过了某个阈值,还没有收到ACK,此时就可以认为出现丢包了,此时就会触发重传。
那么主机A没有收到ACK有两种情况:
主机A发送数据给B之后,可能因为网络拥堵等原因,数据无法到达主机B(数据丢了)
主机B收到主机A的数据之后,做出应答后,应答报文没有到达主机A(ack丢了)
这两种情况客户端都会进行重传数据
站在主机A的角度,A是无法区分是数据丢了,还是ACK丢了,A能看到的都是没有收到ACK。A做的事情就是触发重传。
但是呢,对于情况一,重传数据就好,但是如果出现第二种情况,那么这些数据不是相同了吗,不就出现数据重复了吗?很明显,这是不科学的。
TCP接收方,会针对收到的数据进行去重,会按照序号来进行去重,下面讲讲去重的内部机制。
接受缓存区
TCP传输层内部有一个接受缓冲区。当数据从网络层传递到传输层时,TCP会将数据存入接收缓冲区,直到应用程序通过系统调用(如
receive
或read
)读取这些数据。对于这个接受缓冲区,如果在这内部有一个数据报,再传输进来一个序号相同的数据报,那么接受缓冲区是不会接收该数据报,所以就达成了一个去重的效果,很好的避免了数据重复,这里的判断数据报是否相同是根据数据报中的序号去判断的,而不是载荷中的数据
接受缓冲区,除了去重之外,还有一个很重要的功能,就是针对收到的数据进行排序
因为网络传输可能会后发先至,所以导致数据接受的顺序就跟我们开始发送时不一样,我们肯定希望咱们发出去的数据能够有序的到达接收方,有序的被处理。
所以在接收缓冲区里就会对收到的数据先排个序,让序号小的在前头,序号大的在后头,并且数据和数据之间的序号始终都是连续的,这样就跟初始发送时的顺序一样。
(正常的一个一个传输是不需要再进行排序,因为不存在后发先至,所以接受顺序是跟发送顺序一摸一样,滑动窗口的传输因为存在后发先至,所以要用到接受缓冲区里的排序)
讲完上述这些,我们又会想超时的时间如何确定?
这个时间的长短,随着网络环境的不同,是有差异的。
如果超时时间设的太长,会影响整体的重传效率;
如果超时时间设的太短,有可能会频繁发送重复的包;
TCP为了保证无论在任何环境下都能比较高性能的通信,因此会动态计算这个最大超时时间Linux和Windows 超时都以500ms为一个单位进行控制,每次判定超时重发的超时时间都是500ms的整数倍。
如果重发一次之后,仍然得不到应答,等待 2*500ms 后再进行重传。如果仍然得不到应答,等待 4*500ms 进行重传。依次类推,以指数形式递增。
当累计到一定的重传次数(如Linux中的tcp_retries2
参数,默认为15次),系统会发送带有RST标志位的重置报文来清空中断状态并立即请求主动终止双方的连接。如果RST报文传输过去接受方还没响应,系统会认为网络或对端主机出现异常,从而强制关闭连接(总而言之就是当累积次数过多时,双方就会断开连接,不再进行数据传输。)
连接管理机制
连接管理机制包含建立连接 + 断开连接
之前讲过建立连接和断开连接都是通过操作系统内核完成的,现在来说下是怎么完成的?建立连接通过三次握手完成,断开连接通过四次挥手完成
tcp 这里的握手挥手 其实是给对方传输一个简短的,没有业务数据的数据包,通过这个数据包实现一些操作。完成这些操作后双方就会建立连接或者断开连接握手挥手这种操作,不是 TCP 独有的,甚至不是网络通信独有的.计算机中的很多操作,都会涉及到"握手"这种操作。
下面我们分别详细讲述一下三次握手和四次挥手
三次握手(建立连接)
建立连接就是通信双方各自保存对端的信息,具体完成过程需要经过三次握手。三次握手其实就是客户端服务器三次通信的过程。
三次握手的第一次,一定是客户端先发起的(客户端通过代码去发起建立连接请求,从而在内部开始三次握手)这个数据报不携带任何业务数据,也就是载荷部分是空着的,只有TCP报头部分,这个TCP报头中,其中6个标志位的SYN这一位为1(意味着它就是发起请求的数据报,叫做"同步报文")
这个SYN也就是synchronize,译为"同步",在多线程中的"同步"代表互斥,在TCP中的"同步"则是希望服务器和客户端之间,达成某种配合关系,达成某种有关联的状态。
当客户端给服务器发送SYN(我想和你建立连接),此时服务器就会给客户端回应一个应答报文(ACK),这个应答报文也只是告诉客户端说,我收到你的请求了,这个时候紧接着服务器就会给客户端发起一个同步报文。
当客户端收到服务器发来的请求之后,也会返回一个应答报文。
上述流程,客户端和服务器各自给对方发送SYN,在各自给对方返回一个ACK,那么我们发现,这里面有四次交互,我们不是说三次交互吗?其实中间这两次是可以合并成一次的(提升效率),都是服务器给客户端返回的数据,所以ACK和SYN可以合并成一个网络数据。
在六个标志位中,ACK第二位为1,SYN第五位为1,所谓合并就是让这一个TCP数据报,报头中同时把这两个bit位都设置为1.
所以这样就变成了三次握手(三次信息通信), 上述就是三次握手的基本流程
那么三次握手有什么作用呢?
1.验证双方的接听发送能力是否正常
通过这三次握手的信息交流,确认服务器和客户端的发送能力和接受能力都是正常的,从而为TCP的可靠传输做了保障(如果这接受或者发送能力不正常的话,那么还怎么达成可靠传输)
所以设想一下,三次握手变成了两次握手是否可行?那当然是不行的,此时服务器这边对于"发送能力""接收能力"是不确定的,所以需要第三次交互,确认服务器的发送能力和接受能力。
2.确认通信路径是畅通的
在正式传输业务数据之前,我们可以用报文去确认一下通信链路是否畅通,以便后续的传输
3.协商一些必要的参数
有些参数不是单方面就能确认,需要双方共同来确认出来。要协商的东西有很多,其中TCP通信时使用的序号,就是协商出来的(通常不是0/1)
所以在三次握手后,我们完成了这些操作(以上三个作用),客户端和服务器的连接也就随之建立好了。
四次挥手(断开连接)
对于三次握手,第一次发送请求一定是客户端第一次发起,而四次挥手,客户端和服务器都可以主动发起!
那么怎么主动发起呢?当我们在代码中对socket对象调用close或者直接结束该进程的时候,它就会主动发起第一次请求。
此处以客户端主动提起为例:
当前客户端要进行断开连接,什么时候会触发断开连接,当客户端代码调用 socket.close 或者客户端进程结束,这样的情况就会触发tcp的四次挥手。
一旦close或者结束进程,客户端就会给服务器发起一个FIN(结束报文)数据报,FIN就是TCP中的一个标志位,代表着希望结束这段连接,服务器收到后立即返回ACK,返回应答报文之后,服务器就会给客户端发起FIN报文(这里的发送也是应用端层面的发送,属于我们所控制的,下面会说),接受成功后客户端返回ACK,最终完成断开连接的过程。
TCP中期望达成的效果是双方互删,所以双方都要发送FIN。
那么这里的四次挥手中间两次交互是否可以合并??
三次握手过程中,服务器收到客户端发来的请求后,SYN 和 ACK都是内核自动控制发送的返回,返回的时机其实都是内核控制的,同一时机。
而四次挥手,当服务器收到客户端发来的FIN(假设客户端主动提出断开连接),此时服务器就会立即返回ACK,这也是由内核控制的,当我们在代码中调用close或者直接结束进程的时候,才会触发服务器给客户端发送FIN(应用程序控制的)。这两个操作不是同一时机,中间可能会隔很久。
所以通常情况下是不能合并的,一个是内核立即发送,一个是我们决定什么时候发送。
但是这是通常情况,在特殊情况中TCP里面有一个延时应答机制可以让它们一起发送,该机制可以让ACK在FIN发送时才跟着一起返回出去,从而合并到一块,"延时应答"机制后面会详细讲述。
上述的合并情况,属于"特殊"情况,对一般情况是不能合并的,所以,最终还是把断开连接称为"四次挥手"。
那么这里有个疑问,假设客户端发起了请求,服务器做出了ACK回答,但是服务器接下来一直没close或者结束进程,难道要一直等它发FIN吗?
这是不可能的,这里要说一个重要的状态:CLOSE_WAIT
它是第一次接受FIN报文到第二次自己发送FIN报文的时间,如果这里CLOSE_WAIT时间过长,一直都没有发送FIN的话,那么系统就会直接强制断开双方的连接,对于强制断开连接的话,其实是没什么bug的,但肯定没主动断开连接好。
注意:当代码中调用close并不会开始就执行,而是会先发送一个fin包从而开始执行任务(第二次调用也是同理),只有完成这四次挥手,双方的close才会相继执行(先请求断开的close后执行,对端先执行),执行完后通信双方就会删除对端信息(断开连接)。
除此以外,这里还有一个状态:TIME_WAIT
TIME_WAIT是第二次接受FIN到自己closed的期间时间,那么我们不可以接收到FIN就直接close吗,还非要等一会,TIME_WAIT存在的意义是什么?
TIME WAIT存在的意义,就是为了应对最后一个 ACK 丢包。客户端在收到服务器返回的 fin 之后,不能立即释放 tcp 连接(close),如果立即释放了,后续一旦对端重传了 fin(ack丢包了),此时就因为过早释放TCP连接无法返回 ack 了
因此客户端这边就需要有一个特殊的状态 TIME WAIT 状态来等待可能到达的 fin 重传的数据。所以只有当对面成功接收到ACK,成功close了之后,我们客户端才能紧跟着close,不然就处于TIME WAIT,同样TIME WAIT 状态不是持续的,而是有一定时间的,超出了时间,如果还没closed结束掉,就会强制断开双方的连接,跟CLOSE_WAIT一样,不会产生很严重的后果,但肯定没主动断开连接好。
这两个状态等待的时间都是2MSL,MSL是什么,网络上两个节点通信消耗的最大时间为 MSL,等待时间为其两倍,如果还没接收到,说明没救了,就强制断开。
那么四次挥手有什么作用呢?
四次挥手的设计确保了 TCP 连接的关闭过程是可靠的、有序的,并且能够避免数据丢失、资源泄漏和连接状态混乱等问题,保障了TCP的可靠性。
滑动窗口机制
前面我们讲到的确认应答,超时重传,连接管理都是用来保证TCP可靠传输的机制。
而滑动窗口机制则是用来提高TCP效率的机制。
我们知道对每一个发送的数据报,都要给一个ACK确认应答。收到ACK后再发送下一个数据段。这样做有一个比较大的缺点,就是效率较低,尤其是数据往返的时间较长的时候,跟udp效率简直没法比
所以我们要针对TCP尽可能的提高效率,将它与udp的效率差距尽可能缩小。TCP 只要引入了可靠性,传输效率是不可能超过没有可靠性的 UDP。
这就引出来了滑动窗口机制:
那我们就想,既然这样一发一收的方式性能较低,那么我们一次发送多条数据,就可以大大的提高性能。
滑动窗口便是如此
那么下面我们来详细说下滑动窗口,以上图为例:
该机制最开始会同时发送多条数据,数据量由窗口大小表示:上图的窗口大小就是4000,所以可以最开始同时发送4000的数据量
在发送完初始的数据量后,后续的数据怎么发送呢?
只有当主机A收到序号最小的数据包(1-1000)返回的ACK后,才会继续发送第五个字段(4001-5000)的数据,发送完4001-5000后,才能继续发送5001-6000的数据,同理该数据要收到1001-2000数据包返回的ACK后,才能发送出去。以此类推按顺序执行。
这里看起来的直观效果,这个"窗口"就开始往后"滑动”,就叫滑动窗口机制。
那么这过程中出现了丢包怎么办?分为两种情况:
情况一:数据包已经抵达,ACK被丢了
发送的数据包已经抵达,回应报文ACK却丢了这种情况下,部分ACK丢了并不要紧,因为可以通过后续的ACK进行确认;
这是因为ACK应答报文上的确认序列号表示的是,该字节序以前的报文一全部到达,请下一条报文从该字节序开始。
就比如上图中的1001丢了,但是收到了2001,也就代表2000以前的数据已经全部收到了。所以此时是否收到1001已经无所谓了
情况二:数据包直接丢了
比如上述情况,1001-2000的数据包丢了
当1001-2000报文段丢失之后,虽然主机A一直在给主机B往后发送数据,但是接收端一直没有收到1001这段的数据报,就会一直向发送端索要1001的数据
发送端发现连续三次收到了接收端发来同样一个 “1001” 这样的应答,他就明白了这段数据丢了,就会将对应的数据 1001 -2000 重新发送,这个就叫快速重传
快速重传机制:当发送方连续收到三次相同的失序确认应答(ACK)时,就会认为对应的数据包丢失,并立即进行重传,而不需要等待超时事件发生。这种机制可以快速补发丢失的数据包,提高传输效率
这个时候接收端收到了 1001 之后,再次返回的ACK就是7001了(因为2001 - 7000接收端其实之前就已经收到了)
所以这就是区别于普通传输情况的滑动窗口传输机制。
如果通信双方,传输数据的量比较小,也不频繁, 就仍然是普通的确认应答,按照普通的超时重传处理重传。
如果通信双方, 传输数据量更大,也比较频繁, 就会进入到滑动窗口模式, 按照快速重传的方式处理重传.
流量控制机制
接收端处理数据的速度是有限的。如果发送端发的太快,导致接收端的缓冲区被打满,这个时候如果发送端继续发送,就会造成丢包,继而引起丢包重传等等一系列连锁反应。
因此TCP支持根据接收端的处理能力,来决定发送端的发送速度。这个机制就叫做流量控制
具体如何衡量接收方的处理能力呢?
直接通过接收方的接收缓冲区的剩余空间大小,作为衡量处理能力的指标
剩余空间越大,意味着消费速度越快,处理能力就越强.
剩余空间越小,消费速度越慢,处理能力就越弱.
那么接收端的处理能力怎么返回告诉发送端呢?
接收方每次收到数据之后,都会把接收缓冲区剩余空间大小通过 ack 返回给发送方.发送方就会按照这个数值来调整下一轮的发送速度。
那么缓冲区剩余空间大小在TCP中是什么形式表示的呢?
TCP中的16位窗口大小就是去描述接收缓冲区剩余空间大小
所以接收端将接收区缓冲区剩余空间大小放入 TCP 首部中的 “窗口大小” 字段,通过ACK端通知发送端;
如果窗口大小比较大,则发送方速度就比较快,如果窗口大小较小,则发送方速度就比较慢
如果接收端缓冲区满了,就会将窗口置为0,这时发送方不再发送数据,此时发送方会定期发送一个窗口探测数据段(不携带载荷),从而触发ACK,知道B这边的缓冲情况,接收端把窗口大小告诉发送端,如果此时窗口大小不为0,则会继续发送。
由于窗口大小是16位 ,所以接收缓冲区剩余空间大小是不是只能最多为64k?
其实TCP 报头中,选项部分里有一项是叫做"窗口扩展因子",通过扩展因子, 就可以让窗口大小表示一个更大的值,不是最多只能64k。
拥塞控制机制
拥塞控制和流量控制类似,流量控制是站在接收方的角度从而影响发送方的速度。但是呢,发送方发得有多快,不光要考虑接收方的接收能力,还要考虑中间的路由器/交换机是否能接受,中间这些机器的处理能力是不确定的,链路上的任何一个节点,性能瓶颈都会制约发送方的发送速度。
所以流量控制是基于接收方的处理能力去控制,而拥塞控制是基于通信过程中中间节点的处理能力去控制的。
同样拥塞控制机制中也跟流量控制机制一样,存在一个窗口,叫拥塞窗口,它是TCP协议中用于控制网络拥塞的一个重要参数。用于动态调整发送方发送数据的速率,以避免网络拥塞。拥塞窗口的大小会根据网络的状况动态调整,以优化数据传输效率。那么拥塞窗口的值怎么调整的呢?
流量控制的时候,很容易定量的来衡量其窗口大小(通过缓冲区的剩余大小)。但是在拥塞控制机制中衡量窗口值就比较复杂,因为通信过程中节点里有多少设备?每次走的路径可能都不一样,每个设备的处理能力,繁忙程度都不一样,这样就很难衡量出来
所以我们就不管你中间结构有多复杂,tcp都把他们视为一个整体,然后通过"实验"的方式,去调整拥塞窗口的大小:
慢启动阶段(Slow Start):
在TCP连接建立或重新启动时,拥塞窗口的初始值通常设置为1个最大报文段(MSS)。
每当发送方接收到一个确认(ACK),拥塞窗口的大小会按指数级增长(翻倍)。
这种增长方式会持续到拥塞窗口达到一个预设的阈值或检测到网络拥塞。
拥塞避免阶段(Congestion Avoidance):
当拥塞窗口达到阈值时,进入拥塞避免阶段。
在此阶段,拥塞窗口的增长变为线性增长,每次收到一个ACK时,窗口大小增加一个较小的固定值。
这种方式旨在避免网络过载,同时尽可能利用网络带宽。
网络反馈机制:
TCP通过观察网络的反馈(如往返时间RTT、丢包率等)来判断网络拥塞状态。
动态调整:
当检测到数据包丢失(如通过超时或重复ACK),TCP会认为网络发生拥塞,将拥塞窗口减小到初始值,并重新进入慢启动阶段
通过这些机制,TCP能够动态调整拥塞窗口的大小,以适应网络的实时状态,从而在避免拥塞的同时最大化传输效率。
因为流量控制,拥塞控制都是在限制发送方的的发送速度。
所以最终时机发送的窗口大小,是取 流量控制 和 拥塞控制 中的窗口的较小值,(必须让接收方和网络的中间结点都可以接受,所以必须取较小值,较大值会有一方接受不了)
延迟应答机制
一般情况下,A 把数据传给 B, B 就会立即返回 ack 给 A
也有的时候, A 传输给 B, 此时 B 等一会再返回 ack 给 A,这个就叫延迟应答机制那么这么干有什么作用呢?
延时返回 ack,给接收方更多的时间,来读取接收缓冲区的数据,此时接收方读了这个数据之后,缓冲区剩余空间变大了,返回的窗口大小也就更大了,这就提升了传输效率
捎带应答机制
捎带应答机制允许在数据传输的过程中,将确认应答信息“捎带”在数据包中一起发送,而不是单独发送一个ACK包。这种机制建立在延时应答的基础上,通过将原本需要单独发送的确认应答(ACK)报文与其他数据包合并,从而减少网络中的确认消息数量,提高网络利用率。
更准确的说,四次挥手可以三次挥完,就是捎带应答的体现,该机制能使四次挥手变为三次挥手。
网络通信中,往往是这种"一问一答"这样通信模型。
ack 是内核立即返回的,response 则是应用程序代码来返回的,这两者时机是不同的.
由于 tcp 引入了延时应答, 上面的 ack不一定是立即返回, 可能要等一会,在等一会的过程中,B 就正好把 response 给计算好了,计算好了之后就会把 response 返回,于此同时顺便就把刚才要返回的 ack 也带上了,这两个数据就合并成了一个数据,提升了效率。
面向字节流的粘包问题机制
此处"包"指的是应用层数据包. 如果同时有多个应用层数据包被传输过去,此时就容易出现粘包问题
目前,接收缓冲区中,这三个应用层数据包的数据,就是以字节的形式紧紧挨在一起的.接收方的应用程序, 读取数据的时候,可以一次读一个字节,也可以读两个字节,也可以读 N 个字节
但是最终的目标是为了得到完整的应用层数据包
B 应用程序, 就不知道,缓冲区里的数据,从哪里到哪里是一个 完整 的应用数据包了
相比之下,像 UDP 这样的面对数据报的通信方式,就没有上述问题
UDP 的接收缓冲区中,相当于是一个一个的 DatagramPacket 对象
应用程序读的时候,就能明确知道哪里到哪里是一个完整的数据
那么该怎么解决该问题呢?
- 使用分隔符(在之前的服务器代码中,我们通过scanner中带有的机制,只要读到空白符就结束了,所以我们使用空白符来作为分隔符)
- 约定包的长度,可以在包头的位置,约定一个包总长度的字段,从而就知道了包的结束位置
对于字节流的粘包问题,不是只有tcp才会有,只要是面向字节流的对象,都会有粘包问题
对于自定义的数据格式我们要按照上述的规则自己去解决粘包问题。
而像xml,json, protobuffer这些定义好了的数据格式,本身都是明确了包的边界的
json, xml, yml 都属于是基于分隔符的方式来进行区分的,protobuffer 按照 长度的方式来区分
保活机制
假设出现主机掉电,网线断开的情况:
此时是一瞬间的事情,来不及断开进程,也来不及发送 FIN请求,主机直接就停机了,站在对端的角度,对端都不清楚对方停机了,所以连接还一直保持着,不会断开,那么之后会发生什么情况呢?
这时候就到我们的保活机制出现用处的时候了。
当TCP连接处于空闲状态(即没有数据传输)一定时间后(默认通常是2小时),保活机制被触发。
由一方(通常是客户端或服务器端,具体取决于实现)发送一个特殊的探测报文(可以类比为"心跳包”),这个报文是一个不携带业务数据的TCP报文段,通常是一个带有ACK标志位的报文段。
如果对方正常响应(返回一个ACK报文段),则认为连接仍然有效,保活计时器重新开始计时。如果对方没有响应,发送方会重复发送探测报文多次(通常默认是3次,间隔时间通常默认是30秒),如果多次探测后仍然没有收到对方的响应,发送方就认为对方已经“挂了",并主动关闭连接。
所以根据保活机制可知面对主机掉电,网线断开情况时如果时间过长则会断开连接,时间短则依然保持连接。
所以上述就是tcp的十大机制,对于tcp的报文结构我们还有三个没讲:
一个16位紧急指针和URG搭配使用,属于tcp的特殊情况,这里不过多阐述,还有一个PSH,是催出对方尽快给自己返回回应,这里不详细说了。
TCP和UDP的总结
TCP优势:可靠传输,TCP 适用于绝大部分场景.
UDP优势:更高效率,UDP 更适合于 对于"可靠性不敏感”,"性能敏感”场景
如果要传输比较大的数据包,TCP 更优先(UDP 有 64KB 的限制)如果要进行"广播传输",优先考虑 UDP,UDP 天然支持广播,TCP 不支持 (应用程序额外写代码实现)
有一种特殊的场景,需要把数据发给局域网的所有的机器.这个情况就是广播
4.网络层协议
我们程序员主要还是以 应用层 和 传输层 为主.(都是和未来开发应用程序密切相关的),对于网络层了解下就行。
网络层要做的事情,主要是两方面:
1.地址管理,制定一系列的规则, 通过地址,描述出网络上一个设备的位置2.路由选择.网络环境比较复杂的,从一个节点到另一个节点之间,存在很多条不同的路径,就需要通过这种方式,筛选/规划出更合适的路径进行数据传输
网络层的主要协议就是ip协议,其中又有两个版本:ipv4和ipv6,这里我们主要讲述ipv4版本。
ipv4协议
以下是ipv4协议报文结构:
4位版本:
指定IP协议的版本,对于IPv4来说,就是4。如果是6,就是IPv6(上述是IPv4的报头结构)4位头部长度
描述报文头部的长度,IP头部最大长度是60字节,最短20个字节(因为选项部分所以可变)8位服务类型
其中有3位优先权字段(已经弃用),4位TOS字段和1位保留字段(保留字段留着之后用)
4位TOS分别表示:最小延时(传输过程中消耗时间最短),最大吞吐量(单位时间内传输的数据尽可能多),最高可靠性(降低丢包的概率),最小成本(比较节省系统开销)。
这四者相互冲突,只能选择一个。对于ssh/telnet这样的应用程序,最小延时比较重要;对于ftp这样的程序,最大吞吐量比较重要16位总长度
IP数据报整体占多少个字节(报头+载荷)16位总长度是字节数,也就是前面说的UDP数据报最大64kb,那么这里也是IP数据包也不能超过64kb?
确实是不能超过64kb,IP数据报为了解决该问题,自主实现了拆包组包这样的功能,如果携带的载荷,超出长度上线,IP就会自动拆分成多个数据包,每个数据包携带一部分,发送到对方之后在拼接好。
具体流程:
当前A给B传输一个数据,当前有一个TCP数据包,假设这个数据很长,超过了64kb
A这边再封装成IP数据包的时候,就会把TCP数据包拆成多分(假设3份),使用多个IP数据包进行发送
对于IP数据包来说,他并不关心所要传输的数据里面到底是什么样的,直接简单粗暴的把这个数据分成几份,每一份都装到IP的载荷里面,然后统一发送给B。
那么B是如何区分出拆分出的IP数据报?如何把拆分出的IP数据包进行合并?
IP报头中的16位标识,3位标志位,13位片偏移这三个属性就是用来实现IP的拆包组包的
16位标识:
唯一的标识主机发送的报文。如果IP报文在数据链路层被分片了,那么每一个片里面的这个id都是相同的。就是用来区分哪些数据包要合并3位标志字段:
第一位保留(保留的意思是现在不用,但是还没想好说不定以后要用到)。第二位用来表示该数据包是否需要组包。第三位是结束标记位,当前包是否是最后一个需要组包的部分。13位分片偏移:
就是若干要拼接的数据包的先后顺序,根据片偏移来区分出谁在前,谁在后。8位生存时间:
一个IP数据包,在网络上有一个转发的过程,转发的数据是根据你设定好的目的IP来进行转发,但是如果给定目的IP有问题,一直到不了的话,那会一直存在吗?肯定不会的,所以这里的TTL就是限制一个数据包在网络上转发的最大次数。每经过一个路由器,次数就会消耗掉一次。一般是64(通常情况下64就够用了),一旦达到0,这个数据包就会被丢弃掉。
8位协议:
描述了载荷部分是哪种协议的数据包,也就是交给UDP还是TCP,表示上层协议的类型。一个数据包在分用的时候,要交给上层哪个协议,都是有明确声明的。16位头部校验和:
使用CRC进行校验,这里检验的是报文头部数据是否发生变化,载荷是否发生变化并不管(载荷的检验是到传输层去检验的)32位源地址和32位目标地址:
表示发送端和接收端的ip地址
讲到ip地址,我们又要深入讲一下。
ip地址为32位,那么32位也表示42亿9000万,原则上来说,不同设备,IP地址应该是唯一的,不重复,上述这个数字,在今天来说,显然是不够用的!!!当今社会,移动互联网的发展,一个人就可能两个手机。
那么如何解决该问题呢?
如何解决ip地址不够的问题
共有三个方案。
方案一:动态分配IP地址
某个设备,上网路由器就分配,不上网就不分配(这样的机制只能缓解,不能从根本上解决)。
方案二:NAT机制网络地址转换(网络地址映射)
NAT的核心思想是在一个网络边界路由器上维护一个地址转换表,这个表记录了内部私有IP地址与外部公有IP地址之间的映射关系。当内部网络中的设备需要与外部网络通信时,NAT会将数据包的源IP地址(内网地址)替换为公共IP地址,从而进行通信(内网 IP无法在广域网上使用,所以必须转换)
内网 IP (局域网 IP)如果一个 IP 地址,是以 10.*或者 172.16.*-172.31.*或者 192.168.*(复合上述条件之一,IP 就是内网 IP)
在同一个局域网内部,内网IP之间,不能重复。
在不同的局域网中, 内网 IP之间, 可以重复.内网 IP无法在广域网上使用,只能在自己内部网使用,想和外部交流必须要用NAT转换
外网 IP (广域网 IP)剩下的 IP 就都是外网 IP,外网 IP 则始终都不允许重复
所以当前情况下,通常都是一个小区/一个学校/一个公司,都是构成一个大的局域网 ,这1个局域网中可能就有几干上万个设备,该设备里面的ip用的都是内网ip,而对于这样的一个局域网,就使用一个外网 IP 即可,这样就能进行通信了,内部的IP如果想要和广域网中IP交流则用到NAT转换就可以了。这样就省了很多ip地址出来。
下面我们通过该图片去看下NAT转换
进行IP地址替换,本质上是为了让一个公网IP地址,对应到多个设备,从而起到节省IP的效果.
当IP数据报到达服务器之后,只能看到源IP为1.2.3.4,无法感知最初局域网IP地址了。
从服务器中返回的IP数据报又怎样返回到局域网设备呢?
路由器在进行NAT的时候,会把这次通信的相关信息记录下来,从而能返回成功。
那么如果局域网内有很多台设备呢?
大部分情况下,局域网内的不同设备,都是不同的内网ip,这个时候直接通过内网 IP 就能区分
少数情况下,如果是一个内网ip,就可以按照端口号来区分
极少数情况,碰巧访问的是一个内网ip并且端口相同, 就可以在路由器这边自动映射成不同的端口了,仍然能够区分
注意:在NAT背景下通信
外网设备访问外网设备,不需要任何的NAT,直接就能通信
内网设备或者外网设备访问其他内网的设备是不被允许的。因为我们不清楚对方内网设备所属的外网ip地址,所以不能发送。(知道了就可以发送出去)
内网设备访问外网设备,对应的内网设备的路由器就会触发NAT机制进行IP替换,此时就会给这个网络数据的源IP替换成路由器字节的IP,能成功发送
NAT 机制最大的优势就是局域网内部的设备能够主动访问外网的设备,外网的设备无法主动访问局域网内部的设备(咱们之前写的 UDP echo server 必须部署到云服务器上才能执行就是因为这个,它更好的保护了咱们电脑的安全)
当前的网络世界,就是通过动态分配 + NAT机制解决IP不够用的问题的
NAT机制确实能解决,但是这样的方案又给网络的复杂程度增加了不少,而且也没有重根本上的解决,如果随着设备进一步的增多,一个外网NAT设备上面最最多只能有6w多个表项,NAT也可能不够用了。
所以就需要ipv6了
方案三:ipv6
IPv6是使用16个字节(128位)来表示IP地址的,这就使IPv6可以表示的IP地址个数是一个天文数字了,给地球上的每一粒沙子都分配一个IPv6版本IP地址都是够用的,虽说IPv6可以完美解决IP地址不够用的问题,但是从IPv6被提出到现在真正被使用到的还是非常少的,现实中还是大量的使用IPv4版本的IP地址。
这主要还是IPv4和IPv6不兼容,想要大范围的使用IPv6,就需要将原本的路由器等的网络设备更换掉,这个更换的成本还是很大的,还有原因就是目前IPv6的各方面还没有IPv4一样成熟,效果跟ipv4一样,所以普及程度少。
但是在中国, IPv6 的普及程度是非常高的70%以上注意,虽然 IPv6 看起来没啥收益,但是发展 IPv6 却是咱们国家互联网行业中的一个重要政策,为了反制美国的霸权,IPv4 外网 IP 的分配是掌握在美国人手里的(虽然号称是非营利组织,实际上背后的投资人就有美国军方),ipv4上的DNS域名转换也是如此,一旦得不到IP地址,会对国内互联网行业造成毁灭性打击,所以国家就发展ipv6,很大程度我们在这上面都有话语权,这样就不会被掐脖子了,我们就能掌握ipv6上的地址分配,DNS域名转换等技术。
对于我们电脑里的ipv6一般来说是需要手动设置之后,才能开启的,开启后其实也没什么用。
ip地址的组成
IP地址分为两个部分,网络号和主机号
网络号:用来标识网段(标识一个局域网),保证相互连接的两个网段具有不同的标识;
主机号:用来标识主机(标识一个局域网内部的主机),同一网段内,主机之间具有相同的网络号,但是必须有不同的主机号下面给一个例子:
对于网络号和主机号的划分,主要有两种分类方式,一种是通过IP地址分类(ABCDE),一种是子网掩码。
子网掩码
子网掩码格式和IP地址一样,也是一个32位的二进制数,其中左边是网络号,用二进制数字"1"表示,1的数目等于网络的长度;右边是主机位,用二进制数字"0"表示,0的数目等于主机位的长度。家用网络的子网掩码一般都是255.255.255.0。
IP地址的分类
这五类IP地址的前缀使用来区分类别的,每个类别下,网络号和主机号长度都是固定的。AB类由于主机号太长,实际中很少有那么大的局域网,这就导致很多IP地址会被浪费掉 ,这种分类方式在实际中被淘汰掉了
特殊IP地址
主机号为0的IP:例如192.168.0.0 表示的就是网络号,局域网里不应该存在某个主机的主机号为0
主机号为全1的IP:例如192.168.0.255,假定子网掩码是255.255.255.0,这种称为是广播地址,(广播地址就是往这个地址上发送UDP数据报,此时这个数据报就会被转发给整个局域网中的所有主机,TCP不支持广播。)
环回地址:IP以127开头的,使用最多的是127.0.0.1,是指Windows系统自带的虚拟网卡,这块网卡于任何外部网络不能通讯,只限于本机通讯。
路由选择
网络层有两件事,一件是地址管理,一个是路由选择,地址管理我们刚才提了,现在来讲讲路由选择:
路由选择其实就是对数据在网络中传输的路径规划,数据在发送的过程中,两台主机先是建立连接,两台主机都知道了对方的IP的地址,然后之后的每次数据传输过程中,由于网络环境非常复杂,每个数据报传输的路径不可能都相同,每条数据在网络中进行传输的时候都会经历网络中的中间节点(路由器),这些结点是无法感知到网络环境的全貌的,一个路由器最多只认识它的一些邻居结点(或者是邻居的邻居),也就是说一个网络设备是不可能有数据传输的完整路径,当这些网络设备知道了这个数据报的目的地址(目的IP),具体该怎样走他会向邻居结点进行询问(就类似于我们日常生活中向别人问路)
路由选择的核心思路就是"问路",每个路由器中都会有一个路由表这样的数据结构用来记录邻居结点的信息,当一个IP数据报在进行网络转发的过程中,每个路由器在接收到这个IP数据报之后,就会将这个IP数据报中的目的IP与路由表中的信息进行比对,有匹配的结果就会按照当前路由器规划的路径传输,但是当前路由器的路由表中没有匹配的信息,此时就会走路由器给你指出的一条默认的路径(路由表的"下一跳表项"),这个下一跳表项就会将把这个IP数据报指引向更上一级的路由器(越上一级的路由器中的路由表中记录的信息就会更多),这样直到到达目的IP,当然也有到达不了的情况,因为每经过一个路由器问一次,生存的时间TTL就会减1,如果减到了0还没有到达目标IP,就表示这个包永远到不了了,这个包就会丢弃。
关于网络层协议讲的这些知识点,主要就是有一个简单的了解即可,这些知识主要是“网管"需要重点掌握,咱们作为程序猿就只简单了解即可
5.数据链路层和物理层
数据链路层和物理层的主要协议有两个:以太网协议和WiFi协议,它们两种协议都是横跨数据链路层和物理层的
那么它们有什么区别呢?
以太网协议主要用作有线网络,WIFI协议主要用作无线网络。这里我们主要讲一下数据链路层中的以太网协议。
数据链路层中以太网数据帧的格式:
一个完整的以太网数据帧是由帧头(目的地址+源地址+类型)、帧尾(校验和)和载荷构成。
其中这里的地址指的是mac地址,跟ip地址不一样。mac地址和IP地址是两套独立的地址体系。
IP地址侧重于全局转发,从起点地址到目的地址的转发;mac地址更侧重于局部路径的转发,两个相邻设备的转发。
mac 地址由于是 6 个字节(ip地址是两个字节在ipv4中),能表示的范围比 IP 地址大了很多(是其6w多倍)
IP 地址虽然早都不够用了,但是 mac 还是够用的. 目前来说,每个设备都是有唯一的 mac 地址
,并且在网卡出厂的时候,就写死了其mac地址,一般来说也不能修改,所以mac也可以作为 一台网络设备的身份标识,mac 地址就属于其中的一种定位身份的方式。
mac 地址通常是 十六进制 表示的两个十六进制数字就是一个字节,字节和字节之间通常使用-或者:来分割
这里的类型则是上图中的三种,0800类型则是最普通的数据帧(含有载荷),0806类型的数据帧则是ARP报文, 0835类型的数据帧则是一个RARP报文。这里不详细介绍。
尾部的校验和我就不多说了,都讲过;
所以数据链路层中的以太网数据帧就讲完了,物理层的以太网协议中的数据就更简单,直接变为电信号,光信号去传播,这里就不多说物理层了,没什么好讲的。
6.总结
这就是我们以上的内容,除了应用层,我们把每层的协议都详细说了下,之后下篇文章我们将会重点再说下应用层的其他协议。