目录
5.从浏览器这里输入一个URL,到最终展示出页面,这里大概会发生哪些事情(面试题)
本篇将详细介绍TCP/IP协议中的每一层里面的核心内容
1. 应用层
应用层是和应用程序密切相关的,不同的应用程序,里面可能会涉及到不同的应用层协议。
在写代码时有相当一部分的工作量,就是在自定义应用层协议
自定义应用层协议从两方面考虑:【都是根据需求】
a)考虑要在客户端和服务器之间传输哪些信息
b)考虑信息/数据按照啥格式组织
例子:点外卖
典型场景:展示商家列表
针对这个场景,如何自定义协议
1. 明确传输的信息,请求里有啥,响应里有啥
请求:用户的位置,用户的喜好
响应:商家列表,要包含多个商家信息,每个商家包含:商家的名称、图片、评分、距离
2. 明确数据的格式
网络上传输的数据本质都是bit流(一堆二进制位),也可以把这些数据视为“字符串”
比如:
请求:经度,维度;偏好炒菜,小炒泡馍,面食;
(请求中有两部分,位置信息和偏好信息。中间使用 ; 来分割
位置信息和偏好信息内部的各个部分使用 , 来分割)
响应:XX小炒泡馍,图片,5星,800m;
XX炒菜管,图片,5星,1km;
(响应中包含多个商家信息,每个商家占一行,每行里面多个属性之间使用 , 来分割)
这个格式,在自己写代码时可以根据自己的喜好来写,并不是一定要这样写
但是写代码太灵活了也不好,所以就有一些比较特定的数据传输格式,基于这些常用的格式来传输数据,就可以更通用,也更方便
1.HTTP应用层中最重要的,也最常用的协议
2.XML比较典型的数据组织格式,不过现在不常用了
格式非常有特点,是通过“标签”的形式来组织 键值对 数据的。
例如:商家列表功能
请求:
<request>
<postion>
<jingdu>aaa</jingdu>
<weidu>bbb</weidu>
</postion>
<preferences>
<preference>炒菜</preference>
<preference>小炒泡馍</preference>
</preferences>
</request>
XML缺点:a.丑 b.数据多了编写复杂
c.这些数据要通过网络编程,消耗网络带宽的
由于这里包含了大量的标签,就导致网络带宽占用的的更高了
3. JSON当前最流行的一种数据组织格式。相当于XML的替代品
{
position:{
jingdu:aaa,
weidu:bbb
},
preference:{
'炒菜','小炒泡馍','面食'
}
}
JSON首先是一个{},{}里面包含多组键值对,键值对之间使用,来分割
键和值之间,使用:来分隔
键只能是字符串类型,
值可以是字符串,数字,数组,json
JSON优点:a)可读性非常好 b)看起来美观整洁 c)扩展性强
JSON缺点:引入额外的字符串,传输数据量变大了,消耗更多的带宽
4. 前面的XML和JSON都是属于占用带宽较高,效率较低的格式,但也有些格式,能够更高效的组织数据,其中最典型的叫做 protobuffer(谷歌搞的)
protobuffer既是一个数据格式,同时也是一个库,来操作这种格式的数据
这是一个二进制的格式,这种格式下,组织出来的数据占用空间要比JSON和XML传输效率有明显提升。但二进制不方便阅读,所以这个格式适用于运行效率比较高的场景来使用
2. 传输层
传输层是负责端到端之间的传输,重点关注的是起点和终点
核心的协议有两个:
UDP:无连接,不可靠传输,面向数据报,全双工
TCP:有连接:可靠传输,面向字节流,全双工
2.1 UDP协议
课本上是这样画的,但实际应该是这样的
如果需要使用UDP传输一个比较大的数据,就需要考虑进行拆包,把一个大的数据报,拆成多个小的。但是在应用层这里对数据进行拆包,拆为多个UDP数据报,分别进行传输,开发起来会比较复杂,测试起来也比较复杂,风险较高
所以直接使用TCP,TCP是字节流的,没有对包的长度做出限制
UDP的校验和使用一个比较常见的CRC算法(循环冗余校验)
把UDP报文中的每个字节,都进行累加,加和 也放到 两个字节 的数字中,加的过程中如果溢出了,就溢出,最终得到的就是校验和
2.2 TCP协议(*三次握手四次挥手重点!!!)
TCP的基本特性
面向字节流,有连接,全双工,代码中都是有所体现的
但是可靠传输(TCP中最核心的特性!!!),在代码中体现不出来
可靠传输指的是,数据传输过去之后,发送方知道我发成功了还是没成功
2.2.1 确认应答机制
(1)确认应答机制:我发送一条数据,你给我来个应答(应答报文,也称为ack报文),通过这个应答,我就知道我这个数据是不是发出去了
确认应答机制,就是TCP保证可靠性的最核心机制
(“三次握手四次挥手”只是一定程度上保证可靠性,但这不是核心的!!!)
网络上可能会有一种情况,“后发先至”(后发的请求,可能会先到达对方这里)
网络上通信传输的路径,是复杂的,两点之间,两个报文走不同的路线
但可以在确认应答这个机制中,引入序号来保证不出现歧义(给请求和响应加上编号)
2.2.2 超时重传
(2)超时重传
确认应答描述的是,数据报顺利到达对方,对方给了个响应
但是传输过程中,可能会丢包,如果丢包怎么办
a. 丢包的原因:网络环境极为复杂,我们能上网,是因为接入了运营商的网络。运行商这边就有很多的路由器/交换机,共同组建出一个非常庞大复杂的网络。某个交换机上,不单单是传输我的数据报,也在传输别人的数据报,某个时刻,极多的数据报都要经过这个交换机。交换机的转发能力不是无上限的,很多数据报走到这里,导致达到交换机的转发上限,无法快捷的完成转发了,就可能会导致有一部分数据报就超时了。
b. 如果数据掉包了,此时就要考虑通过“超时重传”来进行了
接收方因为丢失ACK就会导致收到重复的消息,TCP就会针对相同的消息进行去重(根据序号来去重就可以)
c. 超时时间是如何确定的
一般系统里面会有一个配置项,描述超时的时间阀值
比如:第一次出现丢包,发送方就会在达到超时时间阀值之后,进行重传。如果重传的数据仍然无响应,还会继续超时重传。第二次的超时时间一般要比第一次更长。
超时时间并非均等的,而是逐渐变大的(没有具体的数字)
这样重传,重试几次之后,仍然无法传输,就会尝试重置TCP连接(断开重连)
如果还是连不上,此时就直接释放连接(彻底放弃)
2.2.3 连接管理
(3)连接管理:描述的就是TCP建立连接和断开连接的过程
TCP连接只是“逻辑上的”“虚拟上的连接”
a. 建立连接(双方建立一个相互认同的关系,三次握手)
三次握手(本来是四次,但是中间两次,可以合并在一起)
客户端申请一下,尝试建立连接(三次握手只能是客户端先握)
通信双方,各自向对方申请,尝试和对方建立连接,然后再各自给对方回应
建立连接的过程其实是四次的数据交互
从TCP角度看建立连接(面试考!!!)
为啥要建立连接,建立连接的意义是什么
1)检查一下当前的网络情况是否是通畅的
(三次握手建立连接并不传输任何业务数据)
2)三次握手同时也是在检查通信双方的 发送能力 和 接收能力都是正常的
3)三次握手过程中,也在协商一些重要的参数
两个重点的TCP状态:
LISTEN:服务器启动之后,绑定端口之后(new ServerSocket 完成)
表示 手机开机,信号良好,就可以让别给他打电话了
ESTABLISHED(E stable stablished):连接建立好了之后的稳定状态
表示电话接通,可以说话了
b. 断开连接(双方取消相互认同的关系,四次挥手)
通信双方,各自向对方申请断开连接,再各自给对方回应(客户端或服务器不管谁先挥手都可以)
四次挥手 不一定能合并为三步,这是因为一个是操作系统内核负责,一个是应用程序负责,所以不能合并,但TCP里还有
两个重点的TCP状态
CLOSE_WAIT 等待代码中调用close操作
如果服务器上出现大量的CLOSE_WAIT状态的连接,说明代码bug!
说明close没有被及时调用到
TIME_WAIT 主动发起关闭的一份,会进入到TIME_WAIT
A处理完最后一个ACK之后,不能立即释放连接,而需要保持一定的时间
这个是为了万一最后的ACK丢了,还有机会进行重传
2.2.4 滑动窗口
(4)滑动窗口(提高TCP传输效率的有效机制)
TCP能够保证可靠传输,但是失去了效率
所以TCP希望能够在保证可靠性的前提下,尽可能的提高传输效率(尽管TCP再怎么提升效率,效率也不可能超过UDP),这就引用了滑动窗口来提高传输效率
a. 没引入滑动窗口前,效率非常低,一问一答
b. 引入滑动窗口后,批量发送消息
更详细的步骤是这样
前面都是正常情况下。但如果出现丢包 / 乱序 怎么办
丢包两种情况:1.传的数据丢了 2.响应的ACK丢了
1. 数据包已经抵达,ACK被丢了
2. 数据包直接丢了
上面的,只是把丢的数据包进行了重传,没丢的包,并没有重传,(没有做任何重复多余的工作),所以这样的重传,效率是比较高的
这也叫做 “快速重传” (搭配滑动窗口机制的超时重传)
虽然,这里说了快速重传。但并不是说 超时重传 就没作用了
如果传输的数据很多,批量传输,那么自然是遵守 快速重传 的方式
如果传输的数据很少,此时仍然按照 超时重传 的方式进行(比如传输中最后一个丢了,那么这就可以用超时传输了)
TCP滑动窗口,窗口大小越大,确实发送速度会更快,但是窗口不能无限大
发送的速度快了,但是接收方,处理不过来啊,这就会导致接收方就丢弃一部分数据
TCP是要保证可靠性(最重要的)的,TCP还得重传这些数据,(这就是让本来不富裕的接收方,更qiong了)
这就需要再有其他的机制,来对发送发的发送速度,做出限制
2.2.5 流量控制
(5)流量控制:在滑动窗口的基础上,对发送速率做出限制的机制
就是限制发送方的窗口大小不要太大
a. 那么窗口大小,应该多大,怎么来规定这个大小?
这就要问一下接收方,看接收方,认为发多快合适
接收方对于发送方的反制,接收方根据自己的接收能力,来反向影响发送方接下来的发送速率
b. 那么接收方的接收速率,如何进行量化?
接收方使用接收缓冲区的剩余空间大小,来作为发送方发送速率(窗口大小)的参考数值
16位窗口大小+选项(扩展因子)
c. 缓冲区中空闲空间了,如何告诉给A?
流量控制,是通过接收方的处理能力,来衡量发送方的速率的
2.2.6 拥塞控制
(6)拥塞控制:将中间这些结点看为“整体”,进行动态平衡的调整
前面只是考虑了接收方的处理速率,但处理方难道就可以随便的发送了吗?
显然也不是,还得考虑中间这些转发节点的情况
拥塞控制,从小的开始,逐渐变大,如果丢包再变小,反复动态调整【宏观策略】
流量控制,在控制发送方的窗口大小
拥塞控制,也是在控制发送方的窗口大小
有分歧的时候,听最小的
那么TCP实现拥塞控制具体方式是什么? 慢启动机制
上面的这个过程,是TCP传统的拥塞控制的实现(以前是这样搞的)
而后来的TCP也在进行演化,拥塞控制也做出了一些改进
其中的改进就是回归不再回归到初始值了,而是回归到一个中间的值
2.2.7 延迟应答
(7)延迟应答:提高传输效率的机制,又是基于流量控制,来引入的提高效率的机制
延时应答核心思路,只要把ACK返回稍微迟一点,就这个短时间段里面,就可以让应用程序有更多的操作空间,来把缓冲区数据进行消耗,而这个消耗肯定会导致接下来的返回的窗口,比立即返回的窗口就要略大(窗口大了,发送数据就快了,整体传输效率就快了)
2.2.8 捎带应答
(8) 捎带应答,基于延时应答的基础上引入的
捎带应答,不是100%发生的
2.2.9 面向字节流
(9)面向字节流:粘包问题
面向字节流中,存在一个典型的问题,叫做“粘包问题”
粘包问题:TCP自身对于应用层数据报是无法做区分的
UDP面向数据报,是不存这个问题的
一个UDP数据报,就是对应一个应用层报文
当前要想解决粘包问题,就是要在应用层协议这里进行区分
只要定义应用层数据协议的时候,明确包和包之间的“边界”就可以了
典型的方法有两种:
(1)通过分隔符,比如约定使用 ; 作为包的结束标记
(2)通过指定包的长度,比如在数据包的开头位置声明长度
2.2.10 TCP中的异常处理
(10)TCP中的异常处理
a) 程序崩溃了
进程异常退出
操作系统就会回收进程的资源,包括释放文件描述符表,这样的释放操作,就相当于调用了socket的close,执行close就会触发FIN报文,进一步的开始四次挥手
这种情况和普通的四次挥手实际没啥区别
b)正常关机(通过 开始菜单 这种方式来关闭主机)
关机的时候,系统会先强制结束所有的用户进程,和上述的那个进程奔溃类似
系统内核,会进行文件描述符的释放操作,进一步的进行四次挥手
c)主机掉电
非常突然,猝不及防
1)掉电的是接收方,发送方是不知道对面挂了,继续发数据
此时发的数据,没有ack了,发送方触发超时重传
重传几次之后,仍然无应答,尝试重置连接(复位报文段,RST),也会失败,只能放弃连接
2)掉电的是发送方,此时接收方就等着
接收方也不是就一直等待着,而是等了一会之后,就会发送一个“心跳包”,心跳包是周期性触发的,只是一个简单的不携带任何业务数据的包,存在的意义就是确认一下对方是否还在
d)网线断开
情况同主机掉电
只不过通信双方的主机都是好着的,这两端各自按照上述讲的两种情况分别进行
2.3 TCP和UDP之间的对比
TCP在 有可靠性要求的场景 应用非常广泛
UDP对于可靠性要求不高,但对于传输效率要求很高的情况使用非常广泛
比如机房内部的内网传输,不容易丢包,对传输效率要求比较高,就可以使用UDP
传输层也不是只有UDP TCP 两个协议,还有其他的协议
3. 网络层
网络层做的工作,就是在两点之间,规划出一个合理的路径,同时也需要对主机所在的位置进行定义
这里网络层做的工作:1.地址管理 2.路由选择
认识一下IP协议的报文格式
3.1 IP地址不够用的问题解决方案
1.动态分配IP地址
设备上网的时候才分配给IP地址,不上网的时候,就把IP地址回收,给别人用
2.NAT机制
不再强制要求,每个主机都要有独立的IP,把IP地址分为两大类
a)外网IP / 公网IP
b)内网IP / 私网IP / 局域网IP
只要求外网IP 不能重复,内网IP则在不同的局域网中是允许重复的
常见的内网IP 10.* 172.16.* - 172.31.* 192.168.*
当前网络现状的就是 动态分配 + NAT
3.IPV6 能够彻底解决IP地址不够用的问题
IPv4 4字节,32位来表示IP地址 (42亿9千万)
IPv6 16字节,128位来表示IP地址 (42亿*42亿*42亿 *42亿)
所以IPv6 远远超过 IPv4 的可表示IP地址个数
(给地球的每一粒沙子都分配一个IP地址,也是足够的)
但是IPv6总体来说还没有大规模普及,当前网络现状还是 NAT+动态分配,这是因为IPv6和IPv4不兼容的问题,要想升级就要花钱更换设备
3.2 地址管理
IP地址具体的规则
IP地址分为两部分,网络号和主机号
网络号:表示网段,在两个相邻的局域网中,要求网络号是不同的(同一个路由器连接的局域网)
也就是在同一个路由器中,WAN和LAN口得有不同的网络号
主机号:表示主机,同一个局域网中,主机之间的网络号是相同的,主机号必须不同
子网掩码:划分出从哪里到哪里是一个网络号
子网掩码是32位的,左半边都是1,右半边都是0(不会是10混着)
左半边有多少个1,就表示IP地址左侧的多少位是网络号
(把子网掩码和IP地址进行按位与运算,得到的结果就是网络号)
还有一种网络号和主机号的划分方式(这种方式已经特别老了,现在不用了)
这种分配方式比较死板,而且比较浪费(比如B类中,我这里只有20个设备,把这个网络号分配给我后,主机号是6w个,实际我只用了20个)
特殊的IP地址
(1)如果一个IP地址,主机号为0,此时这个IP就表示网络号 192.168.0.0,代表当前局域网
(2)如果一个IP地址,主机号为1,此时这个IP往往表示这个局域网的“网关”,
192.168.0.1代表局域网的网关(通常就是路由器的IP,可以手动修改)
网关:网关的角色一般就是路由器,看守当前局域网和其他局域网之间的出入口
(3)如果一个IP地址,主机号为全1,此时这个IP表示广播IP
(4)127.*开头,都是“环回IP” 典型的就是127.0.0.1,实际上,只要是127开头的都是环回IP
3.3 路由选择
可以比较于,我要去某个地方,但不知道路怎么走,只能一路走一路问
IP数据报中,就包含了目的IP(我要去哪里),网络数据报到达路由器的时候,路由器自身有一个“路由表”数据结构,(路由表就是这个路由器认识的路),一个路由器无法认识到网络的全貌,但是可以认识附近的一部分
如果当前的目的IP路由器认识,就会给出一个明确的路线
如果当前的目的IP路由器不认识,路由器就会把数据报发给一个“更见多识广”的路由器
(在路由表中有一个默认的选项,下一跳)
但也有可能问了一圈后,也没找到目的地怎么走,比如IP地址不存在(或者不可达)。当数据报转发的次数超过TTL就会把数据报抛弃
路由过程不单单是去找一条路,也是需要筛选一条更好的路
最关键的是路由表数据结构是啥样的,以及路由表数据是咋来的
4. 数据链路层
数据链路层最关键的协议就是 以太网
mac设计的时候是6个字节,所以当前mac地址还是够用的,是可以让每个主机都有独立的mac地址,这个地址是网卡出厂的时候就写好了的
由于当前mac只是在数据链路层使用,只要相邻区域内的设备mac地址不重复就好
因此在网络上,mac地址也可以作为是主机身份标识的一种方式
以太网,就属于传输过程中的基础设施了
把物理层比喻成公路,数据链路层就相当于跑在公路上的卡车
数据链路层里有很多协议,不同的协议,相当于不同型号的卡车,有的卡车载重量大,有的小
以太网就属于载重量比较小的(1500单位是字节)
此时如果要想运输更多的数据,就需要分多个卡车来拉货(IP协议分包保证的)
5.从浏览器这里输入一个URL,到最终展示出页面,这里大概会发生哪些事情(面试题)
这个题有很多角度都可以回答,如果从后端开发来看的话