前言
前面讲的 IO 和 网络初识,比较基础和简单,只是让大家喘口气。。。 网络socket编程,难度中等,大家仔细学,也是能学会的。 ? 接下来这部分,也就是本篇博文。所讲得网络原理更加底层的东西,非常类似于多线程进阶,也就是为了应付面试的八股文。
前面的多线程进阶就是纯八股,而网络原理,其实还有比较清晰的脉络的, 相比前面还是比较好理解,但是也是有一定难度的。
注意本篇博文,几乎是面试必考的内容!
TCP/IP协议
下面,以五层模型为主,我们开始从上往下的顺序,来认识这五层协议。
?
应用层
程序员最经常打交道的一层,其他四层都是操作系统,驱动,硬件,实现好了的。我们不需要去管。【除非你是做系统工程师,驱动开发工程师。。。】
在应用层这里,最最重要的事情,就是“设计并实现一个应用层协议”。 设计并实现一个应用层协议:就是自己去发明一个协议,并实现它。 ? 但是不想要被这件事给唬住了,这其实是一个比较简单的事情,同时也是在工作中经常要做的事情。
?
常见的几种的协议模板
xml
xml 是属于一种比较老牌的数据格式了。 现在虽然也在用,但是用的越来越少了。 xml ,正是因为 它这个繁琐格式,导致它的热度逐渐就降低了。 因此,xml 现在很少作为 应用层协议的设计模板了。 现在使用 xml,主要是作为一些配置文件。 因为配置文件不需要网络传输,繁琐一点也无所谓,毕竟机械硬盘不值钱。
?
json
json 是当下最流行的一种 设计应用层协议 的数据格式。 我们以后在工作中是会经常用到这个。
?
Protobuffer
为了解决 json 的问题,Protobuffer 也就是应运而生。 Protobuffer 是一种二进制格式的数据。
在 Protobuffer 的 数据中,不再像 json 那样 包含 key 的 名字了,而是通过顺序以及一些特殊符号,来区分每个字段的含义。 同时再通过一个 IDL文件,来描述这个数据格式(每个部分是什么意思),IDL 只是起到一个辅助开发的效果,并不会真正的进行传输。 ? 传输的只是二进制的纯粹的数据。
?
小结
这里面 json 的 应用范围 要比 Protobuffer 更广。
通常 开发效率 重于 运行效率的。 ? 开发效率 包含 开发 与 调试。 ? 如果线上环境出问题。 如果使用 json,出问题的请求和响应,一目了然。 如果使用 protobuffer,二进制数据,肯定是没有办法用眼去排查问题的。 为了解决这个问题,只有一个办法:为它专门写一个调试代码,用它去调试,这就更麻烦一些。 ? 这就是我们所说的开发效率上的差异。
?
总结
设计应用层协议,是一件非常普通的事情,也是一件并不复杂的事情。 设计应用层协议,要做的工作 1、明确传输的信息(根据需求) 2、明确传输的模式(参考现有模板:xml,json,Protobuffer) 但是除此之外,业界也有一些现成的,已经被设计好,已经广泛使用了的应用层协议。 (也不是所有的时候,都需要从零设计,很多时候,可以直接基于现成的协议,稍加修改,稍加扩充,进行这种二次开发) 【类似于B栈鬼畜视频,就是典型的二次创作(二次开发),使用现成的视频进行修改剪辑的产物】 ? 在 “现成的协议” 中,最知名的应用层协议,当属 HTTP。
随便打开一个任意的网页,其中的交互过程,都是通过 HTTP进行的。 再比如:打开手机 APP,比如饿了么,滴滴打车等等… 这时候,我们的手机APP 和 它的服务器之间交互过程,很可能也是 HTTP.
?
传输层
虽然传输层是操作系统内核实现的,程序员不需要直接和传输层打交道,但是传输层对我们来说仍然是意义重大的!!!
进行网络编程都需要用到 socket,一旦你调用 socket,代码就进入到传输层的范畴。 如果一切顺利,就还好。 一旦代码出现一些bug。为了解决理解这些bug,传输层的一些知识就是必要的。 传输层的协议,也是面试中特别爱考的,TCP协议、
下面正式开始。 传输层的协议有很多,其中最常见的就是 UDP 和 TCP 协议。 下面,我们就来学习 UDP 和 TCP 中的一些具体知识。
?
UDP
UDP协议端格式
学习一个协议,很多时候就是在研究报文格式。 一个UDP数据报,具体长什么样子的?我们把它搞清楚了! 其实它这里面包含的一些功能特性,也就出来了!
?
UDP的特点
网络编程(TCP 与 UDP协议)中讲了
?
基于UDP的应用层协议
NFS:网络文件系统 TFTP:简单文件传输协议 DHCP:动态主机配置协议 BOOTP:启动协议(用于无盘设备启动) DNS:域名解析协议
?
TCP(重点)
TCP,即Transmission Control Protocol,传输控制协议。人如其名,要对数据的传输进行一个详细的控制。
TCP 是一个非常非常重要的协议,不光在实际开发中广泛使用,同时也是面试中的高频问题
TCP协议段格式
所以,我们整体看一下,发现:在真正理解TCP的一些机制之前,这里面的很多信息,以目前的知识储备是无法理解的。 因此,光看报头结构,还不够!!! 还得结合TCP这里面的一些具体的工作机制,进一步理解 TCP是怎么工作的。 然后,才能去明确 TCP报头,这里面都在做些什么。 这就是我们接下来要完成的工作。
?
下面,我们就来介绍 TCP 里面的十个核心特性
?
第一个机制/特性:确认应答
保证 TCP 可靠传输的核心机制。
在上篇博文网络编程(TCP 与 UDP协议)中,讲到:可靠性是指 发送方发出数据之后,能够知道对方有没有收到数据。 换个说法: 如果发送消息之后,能够知道对方 收到/没收到 数据,就是可靠的。 反之,发送消息之后,不知道对方 收到/没收到 数据,就是不可靠的。
所以,我们的TCP就是为了解决这个可靠性问题。 那么,问题来了:怎么解决的呢?怎么知道对方 收没收到数据呢?
关键就是:接收方 收到消息之后,就给发送方返回一个应答报文(ACK - acknowledge),表示自己已经收到了。 ? 这就跟平常打电话一样,我说一句话,你回一声嗯,或者回一句话。 这样我们就能知道对方是听到我说的话。【知道对方收到了数据】 如果我们了一大堆,对面什么话都没说。 我们 就会认为对面可能没有听到,然后,就会问:你能听见吗?你怎么不说话?是信号不好吗?【知道对方可能没有收到数据】 ? 这里的 “嗯/一句话 的回答”,就是确认应答的关键所在,
?
二、超时重传
相当于对确认应答进行了补充 。
确认应答是在网络一切正常的时候,通过 ACK 通知发送方,我收到了。 如果出现了丢包的情况,超时重传机制就要起到效果了。
?
小结
基于上述的两个机制,TCP 的可靠性,就得到了有效的保障。 一个是针对 顺利的情况下,它是怎么处理的,这就是确认应答。 一个是 针对 通信丢包了,是怎么处理的,那就是超时重传。 这两者相结合,其实我们就可以解决很多很多的问题。 但是呢!TCP 这样的协议,它是不满足于上述这两点的。 它希望它能做得更好,更加极致。
?
下面再来看 第三个机制:连接管理(非常经典的面试题)
连接管理,也是 TCP 保证可靠性的一个机制。
虽然说,它不像上面的两个机制,来得那么直接。 '但是它的存在,也是非常有必要的。
连接管理,说具体点: 1、两个设备之间是如何建立连接的? 2、两个设备之间是如何断开连接的? TCP 连接管理,是网络部分最高频的面试题,没有之一!!!
?
1、两个设备之间是如何建立连接的? - 三次握手
三次握手:客户端和服务器之间,通过 三次交互,完成了建立连接的过程。 这里的“握手”是一个形象比喻。
其实一次握手,就是一次交互的过程。 就是客户端 给 服务器 发了一个数据,这就相当于一次握手。 服务器再给客户端反馈一个数据,这就是另外一次握手。 客户端 根据服务器的反馈,而进行反馈,告诉服务器,它接受到了它的反馈, 一共经历3次,就完成这个三次握手。
结论: 三次握手 对于 TCP 可靠传输来说,是非常必要的,尤其是这个“投石问路”的过程,非常重要!
?
经典的面试
1、描述 TCP 三次握手的过程。
这个我在前面讲的很明白,说不出去,自己去拿块豆腐,自行了断。 我要说的是:针对这个问题,不要用嘴说!要画图!!! 用嘴说,万一说错了,容易把自己绕进去! 画图更稳。 其实 TCP 三次握手的图,有很多版本。 我推荐的是最简单的一个版本。 还有在 SYN 和 ACK 上面标记上序号 和 确认序号。 还有标记上 TCP 转态转换的。(上面复杂图包含这一条) 还有标记对应的 socket API 的(上面复杂图包含这一条) 等等… ? 主要是因为:这水太深,怕你把握不住。【说实话我也把握不住。】
2、为什么握手三次?两次行不行?四次行不行?
首先,4 次是可以的,但是!没必要! 三次握手 本就是一个双向奔赴的过程,本来就是 4次交互。 前面也说到了 中间的那两次是可以合并,没必要分成两次,来降低执行效率。 ? 两次,这绝对不行的。 如果是两次,意味着 服务器(乙方),只能确定它大的接收能力是正常的,发送能力是无法确定的;而客户端(甲方)的接收 和 发送能力都是正常的, 此时,服务器对于进行可靠传输,心里是没底的。 因为它的的发送能力是无法保证的。 故,进行第三次握手,就是为了给服务器吃一个定心丸。 告诉它目前没有任何问题,放心大胆的进行后面的操作。
?
2、两个设备之间是如何断开连接的?- 四次挥手
<font size4=> 三次握手,就让客户端和服务器之间建立好了连接。 其实建立好连接之后,操作系统内核中,就需要使用一定的数据结构来保存连接相关的信息。
其实理解起来很简单。 客户端 和 服务器之间建立好连接了。 那么这个时候,可能你就要问服务器了,水跟你建立的连接的? 服务器回答:哦,是跟那个客户端建立连接的。 总之,四次挥手,它其实和三次握手起到的效果是有很大区别的!!! 三次握手,是进行可靠性传输之前的验证。 而四次挥手,是为了释放必要的资源。
那么,4次挥手,又是怎么挥的呢? 虽然它的目的 和 三次握手 不同,但是流程非常相似! 其实4次挥手,有复杂的图。(带有状态转换 和 还有TCP状态对应操作系统的API)
?
小结
TCP 虽然可靠性是最高的机制,但是TCP也会尽可能的提高效率!! 也就是说:除了刚才的可靠性之外,还回在引入其他的机制来提高传输的效率。
?
第四个机制:滑动窗口
滑动窗口存在的意义就是在保证可靠性的前提天,尽量提高传输效率!!!
?
五、流量控制
流量控制,是滑动窗口的延伸,目的是为了保证可靠性。
在滑动窗口中,窗口越大(一次传输的数据量),传输速率也就越高。 ? 那么,有些朋友就会这么去想:我把窗口弄得越大越好,这样的我们数据传输量就大幅度提升了。 答案:不行! 把窗口弄大,不光要考虑发送方,还得考虑接收方。 如果发送方发送的速度非常快,接收方完全就处理不过来,接收方就会把新接收到的包给丢了。 那么,发送方是不是还得重传,这就不就在浪费时间和资源嘛! ? 举个例子: 这就像有些父母,让自己的孩纸报N多的补习班,时间安排的非常满。 完全不管孩纸能不能接受,也不给孩纸消化的时间。 让孩纸长期处于超负荷状态,此时,孩纸再去学,是学不进去的。 而且前面所学的知识,也不牢靠。 又需要重新学,真是人间地狱啊!
流量控制的关键,就是要能够衡量接收方的处理速度。 怎么去衡量呢?
我们有一个明确的指标。 此处,我们就直接使用接收方 接收缓冲区 的 剩余空间大小,来衡量当前接收方的处理能力。 知识点:多线程基础篇中实战案例讲到了阻塞队列
?
六、拥塞控制
拥塞控制,也是滑动窗口的延伸,也是用来限制 滑动窗口 发送的速率。 拥塞控制,衡量的是 发送方到接收方,这整个链路之间,拥堵情况(处理能力)。 根据这个情况,我们来去决定发送的速率是多少。 举个例子来理解上图:谈恋爱 .>>刚开始谈恋爱的时候,两个人的感情几乎是呈直线上升的。(指数增长) 相处的时间久了之后,两个人的感情上升速度就没有刚才那么快了。(阈值) 处于一个平稳上升的状态。(线性增长) 但是也不是一直都在上升,两个人在一起,总会摩擦。(丢包) 一个不小心没处理好,就会分手,(回到初始窗口大小) 但两个人冷静之后,发现自己都有错,于是道歉和好。 感情又会进入直线上升的过程… 上面的图也就是这么个意思。
?
七、延时应答
延时应答 相当于是 流量控制的延伸。
流量控制 相当于是 踩了一下刹车,使发送方,发的不要太快。 延时应答,就想在这个基础上,能够尽量的再让窗口更大一些。
延时应答,具体延时多少呢? 1、数量限制:每隔N个包就应答一次; 2、时间限制:超过最大延迟时间就应答一次
?
八、捎带应答
捎带应答,又是延时应答的延伸。
?
九、面向字节流 =》粘包问题
不仅仅 TCP 存在粘包,其它的面向字节流的机制也存在。 比如:读文件。
细节拓展: 粘包,这两个字。 有的人可能会有所疑问:为什么要说这个? 这是因为有些人 读这个 粘 字,是 nian 第二声。 实际是读zhan 一声。 但是!无论你读哪一个发音,都没问题! 问题就出在:你要知道这两个发音,都是指的同一个问题。
假设:你只知道 粘包问题 的 粘 读 nian。 1、面试官问你:什么是 粘(zhan)包问题的时候, 你是一脸懵逼的状态。 2、不要和 面试官去争这个东西,不然offer会飞走。 他读什么。你读什么!
?
十、TCP的异常处理
?
1、进程终止
?
2、机器关机
按照操作系统约定的正常流程关机。 无论是关机,还是重启。 都是属于正常流程的关机,会让操作系统,杀死所有进程,然后在关机。 这就相当于回到 原因1【杀死进程】, 杀死进程,就会执行4次挥手。 4次挥手之后,才会真正进行关机
机器关机 的本质上 是 与 杀死进程 是一样的。 都是背刺的失败者!!
?
3、 机器掉电/网络断开
掉电,就是 台式电脑直接拔插头,电脑“嘭”一声,直接关机了。 这次才是真正完美的背刺!!! 因为成功了! 操作系统在这种情况下,是不会有任何反应时间!更不会有任何的处理措施! 因为这个意外实在是太突然了!!!
?
总结
上述的 10个 TCP 特性,除了 粘包问题 之外。 其余九个都是为了:在保证可靠性的基础上,提升效率。
?
TCP vs UDP
1、什么时候使用 TCP? 对可靠性有一定的要求的场景。日常开发中的大多数情况,都是基于 TCP。 ? 2、什么时候使用 UDP? 对可靠性要求不高,追求执行效率的场景。 比如:机房内部主机之间的通信,尤其是 分布式系统中。
由于机房内部的网络环境比较简单,网络带宽也高。 出现丢包的几率很小!并且想这种机房内部对于执行效率的要求也是蛮高的。 这个时候使用 UDP 更加合适!
?
经典面试题
基于UDP 如何实现可靠传输?(看起来是在考UDP,其实是在考TCP) 答案:就是“抄作业”!!!! 本质上就是在应用层基于 UDP 复刻 TCP的 机制。 保证可靠性 最重要的机制:确认应答 和 超时重传,你可以实现一下。 实现的同时,要确保这里面 引入 序号 和 确认序号。 再进一步确保可靠性:引入 连接管理(三次握手,四次挥手) … 反正就是将上面的十个机制,都实现一下。 用代码去实现,也就是在应用层上实现。 ? 这样的 UDP 与 TCP的区别就在于: TCP 这十个机制都是操作系统内核代码实现好了的。 UDP 是我们手动敲出来的 十个机制。
传输层协议,只有 TCP 和 UDP吗? 像当下常见的 LOL,Dota2,吃鸡,王者荣耀…这些对抗性很高的游戏。 它们底层使用的 TCP,还是UDP? 答案:都不是! 既要保证可靠性,又要保证效率。 除了 TCP 和 UDP 之外,还有其它的一些协议。 有兴趣的,可以自行了解。
?
网络层
网络层最重要的协议就是 IP协议。
毕竟 TCP 和 IP 被称为 “冠名协议”。 我们现在学习的叫做 TCP/IP 协议栈。 所以肯定是 TCP 和 IP 是 最重要的协议。
IP协议的复杂程度,甚至比 TCP协议 还要高。 但是,我们不并作过多深究。
针对 IP 协议,主要完成的工作有两方面: 1、地址管理 2、路由选择
?
先来看一下IP协议的报头结构
?
IP协议主要完成两方面工作
1、地址管理
IP 地址 是一个 点分十进制 构成的数据。 对于这个IP地址来说,它这里面还可以进一步的来进行划分 IPv6 最关键的要点:使用了16个字节来表示IP地址。 虽然非常够用,但是受限于升级的成本。 以至于说:站在全世界的角度上来看,IPv4 还是站在一个主流地位。 因为,本篇博文仍然是以 IPv4为 主。
?
2、路由选择
路由选择,也就是规划路径。
当两个设备之间,要找出一条通道,能够完成传输的过程。 要想找出通道的前提是:要先认识路!
?
数据链路层
这一块,我会讲的很简短。 因为 数据链路层 已经是离我们程序 “非常远”了。 ? 数据链路层主要的协议,叫做“以太网”。
像平时我们插的网线,就叫做“以太网线”。 至于 以太网的协议,我们不做过多讨论。
我们了解一下“以太”: 以太,本来是物理学上的概念。
就是说,以前人们对光的认识,不太深刻。 认为 光 也是一种波,必须要依靠介质才能传播。(类似声波) 后来,发现光也能在真空中传播。 当时的人们就蒙了。【光没有介质,也能传播的嘛?】 于是,就脑补出了一个介质:以太 认为“以太”,这一种介质在宇宙中无处不在。 光就是通过“以太”来进行传播的。 ? 再往后,人们发现“以太”,这个说法并不靠谱。 这是因为两个人的存在:迈克尔逊 和 莫雷。 迈克尔逊 和 莫雷,这两个人做了一个实验。 证明了 光速在各个方向上,都是一致的。 【实验借助了地球的公转的速度,来进行验证。】 【根据 顺着公转 和 垂直公转,进行测量光的速度,发现都一样】 【如果存在介质,就是势必有些地方是分布不均匀的,这就会导致传播的速度是不一样】 ? 可以说这个实验,就是间接的推翻了“以太”。 进一步就衍生出了 “狭义相对论”。 ? 总之,以太 就是 人们的 一个臆想 / 一种假设。 但是这个概念,被保留了下来。 在我们的网络里,就把当前的数据链路层的协议称为“以太网”。
以太网,这个协议不仅仅规定了数据链路层的内容。 也规定了物理层的内容。 以太网,是一个横跨两层的协议。
以太网,最核心的数据结构“以太网数据帧”。
?
总结
当前位置,已经把网络中的一些核心内容,讲的差不多了。 其中 TCP 和 IP,是我们需要掌握的最最最重要的协议。 因为我们的协议栈,就叫做 TCP/IP 协议栈。 尤其是TCP特别重要。
?
补充:特殊协议
?
DNS
DNS 是一个应用层协议。 它的作用:域名解析。
?
NAPT
这个就是我前面讲到 端口号重复的情况,它的处理机制就是 NAPT。 这种关联关系也是由NAT路由器自动维护的。 例如在TCP的情况下,建立连接时,就会生成这个表项; 在断开连接后,就会删除这个表项
|