网络原理(一)目录
- 网络原理
- 应用层
- 传输层
- 先说UDP(不可靠传输)
- 重点说明TCP(可靠传输)
- 一、确认应答
- 二、超时重传
- 三、链接管理
- 建立连接
- 断开链接
- 四、滑动窗口
- 五、流量控制(也是保证可靠性的机制)
- 六、阻塞控制
- 七、延迟应答(效率机制)
- 八、捎带应答(效率机制)
- 九、面向字节流(粘包问题)
- 十、异常情况(心跳包)
网络原理
网络协议的在实际运用是分为5层协议及:
- 应用层
- 传输层
- 网络层
- 数据链路层
- 物理层
这五层结构在,java 网络编程中已经有所现,具体用法具体实现的功能,都有。
应用层
这里主要的一个协议也是目前网络上最常用的一个协议,HTTP协议。
这层结构,决定数据要传输什么,拿到数据后如何使用。HTTP为什么是最长用的一个应用层协议,其本质就是在确定框架后,程序员可以在自定义一些协议,可控性和操控大大提升。
及约定数据报的数据格式,就是在自定义协议。
而如何约定?
- 确定要传输那些信息(根据需求走)
- 确定数据按照啥样的格式来组织。
-
网络上传输的,本质都是二进制字符串,就需要将上述的穿输的信息整合为一个字符串。但是在传输内容的时候,一个我们需要的数据,其实和其他数据是合在一起的。我们要如何拿到所需要的数据。很简单,我们在传输数据的时候,设定一个符号,或者距离单位。锁定所需要的数据。
-
比如我规定,属性之间用 ’,‘ 隔开 ,每个对象用 ‘\n’ 隔开,结束标志用 ’;‘ 隔开 。
-
只要发送方发送数据按照这个格式传输,然后接收方,在解析数据的时候,用这个格式解析就好;
-
在开发中,有一些特定的现成的格式。可以直接拿来使用。比如之前的一种典型的格式,xml ,还有现在用的比较多的一种格式。json,
什么是json。
{
userId:100
userPos:10-100
}
使用{}作为标识,{}里面的诺干个键值对,每个键值对用 ’,‘ ,分割,键值对,用 ’:‘ 分割。 键必须是字符串,值就可以是一个object。
传输层
先说UDP(不可靠传输)
这个就是UDP 的报文格式。
- 端口是2个字节,所以端口可以取:0-- > 65535
- 报文长度就局限了正文最大能装多少的内容。64KB。所以如果用UDP进行传输一个很长的数据,就需要包一个较大的数据,拆成很多分,用UDP传输。(很复杂)
所以用UDP传输数据,不能太大,否则就会出现问题。 - 校验和,是为了校验数据的准确的。在真实的网络传输中就会遇到很多问题。磁场,太阳风暴,等等。这些都会干扰数据的稳定。而校验和就是用来判定,当前的数据是否出错。(通常是设定一种特殊的算法,比如取正文中的一些字符,然后算出一个数据,然后传输完毕后在验证一次)
重点说明TCP(可靠传输)
TCP如何实现可靠传输?
一、确认应答
什么是确认应答呢?其实就很简单,比如网上购物,商家发货,货物根据你的信息发送货物,然后送到哪里并且,你确认了收货,然后平台就会将钱给商家,这就是一个很简单的应答模式。
这个模型有一个问题,就是网络的状况有很多种,会出现后发先至的问题。对应到上述的例子就是,遇到强降雨,但是后边的货车走了另一条路。走的就比之前的车要快。
异常状态
正常状态
如何解决这个问题?
此时就需要对消息进行编号。
- 给发送的消息分配一个需要
- 同时应答报文,给出一个确认顺序
协议格式:
TCP将每个字节的数据都进行了编号,基序列号。
- 源/目的端口号:表示数据是从哪个进程来,到哪个进程去;
- 32位序号/32位确认号:后面详细讲;
- 4位TCP报头长度:表示该TCP头部有多少个32位bit(有多少个4字节);所以TCP头部最大长度是15 * 4 = 60
- 6位标志位:
- URG:紧急指针是否有效
- ACK:确认号是否有效
- PSH:提示接收端应用程序立刻从TCP缓冲区把数据读走
- RST:对方要求重新建立连接;我们把携带RST标识的称为复位报文段
- SYN:请求建立连接;我们把携带SYN标识的称为同步报文段
- FIN:通知对方,本端要关闭了,我们称携带FIN标识的为结束报文段
- 16位窗口大小:后面再说
- 16位校验和:发送端填充,CRC校验。接收端校验不通过,则认为数据有问题。此处的检验和不光
- 包含TCP首部,也包含TCP数据部分。
- 16位紧急指针:标识哪部分数据是紧急数据;
- 40字节头部选项:暂时忽略;
首先接送方的序号和发送方的序号无关
确认序号 1001 的含义。
- 小于1001的数据,我已经收到
- 我接下来想要发送方从1001开始的数据
在之前说过,网络传输的时候,会有后发先至的情况,同样的这个也是。但是能,TCP在传输时会有序号,序号天然就有顺序,所以对于这种情况,只要在接收方接收前排序就好了。(优先级队列就能完成这个)、
对于TCP来说,自身也承担了这个整队的任务,TCP会有一个缓冲区(内核中的一个区域),每个socket都有自己的缓冲区。然后TCP就可以按照序号针对收到的消息进行针对。
如果一切顺利就可以应答了。可问题就是,现实并不顺利。比如丢包。
二、超时重传
那么什么是丢包呢?
传输数据的时候要经过各个节点,可是问题来了,一台机器节点的转发能力是有极限的,也就是说到达极限的机器,就可能会引起丢包的问题。(实时性的APP或者应用对于丢包的问题非常的敏感),如果丢包了接收方就收不到了。自然就不会返回ACK。
如何解决?
之前的逻辑,就是发送方发送消息会受到一个接收者的反馈的接收信息。但是丢包了,数据就不完整了。在队列里之前排好序的数据,因为丢包使得,序列并不完整。
此时因为序列并不完整,就不会交给接收者。然后序列就会等待,如果此时的序列等待时间长了,发送方迟迟拿不到应答。那么发送方就知道,传输出了问题,那么就会重新再发一次。直到拿到应答。(这个就叫超时重传)
有一个细节:
- 数据直接丢失,接收方没收到,自然不会发 ack
- 接收方接收到数据了,但是返回的ack 却丢了。
此时发送方分不清这些情况,只能重传。
可是此时,接收方已经在缓冲区中有了数据,再传一个,就重复了。此时接收方的缓冲区,会根据数据的序列,自动去重。保证应用程序中读到的数据任然只有一份
如果多次重复发送还没有收到ack 那么多半网络出了问题。主要是假设丢包率为10%,那么连续两次丢包,概率就是10% * 10%=1%
而以上这两种机制就是,TCP的可靠性保障。及:
- 确认应答
- 超时重传
三、链接管理
TCP创建链接:三次握手
TCP断开链接:四次挥手
建立连接
什么是三次握手?
握手是指通信双方,进行一次网络交互。相当于客户端和服务器之间,通过三次交互,建立连接关系(双方各自记录了对方的信息)
互相应答,确保双方的网络是完整的。并建立连接(有那么一滴滴可靠性的说法,但并不是)
等建立连接完毕,服务器 accept 把建立好的链接从内核拿到应用程序中。
- 此时如果ACK 为 1 ,则表示当前的TCP数据报为一个应答报文
- 此时如果SYN为 1 ,则表示当前的TCP数据报为一个同步报文
- 如果ACK和SYN都是 1 ,则这个报文就是 SYN + ACK
做这么多。其实就是验证自己的发送能力和接收能力是否正常。
断开链接
什么是四次挥手?
通信的双方,各自给对方发一个FIN(结束报文),在各自给对方放回ACK
建立连接,一定是客户端发起,但是断开链接客户端和服务器都有可能。(并且通常ACK与FIN并不能重合)(FIN比特位位1的时候)
注意:
三次握手:ack与syn是同一个时机触发的(都是由内核完成)
四次握手:ack与fin内是不同时机出发的。前者由内核完成,而后者需要程序中 socke t的 close 方法才会触发fin、
四、滑动窗口
是为了解决,数据传输的效率问题。
对每一个发送的数据段,都要给一个ACK确认应答。收到ACK后再发送下一个数据段。这样做有一个比较大的缺点,就是性能较差。尤其是数据往返的时间较长的时候。
可靠性的提升,往往代表,效率的损失。
可以看到,客户端A这边传输5000个字节,要应答四次。此时A就用了大量的时间去等待ACK
既然这样一发一收的方式性能较低,那么我们一次发送多条数据,就可以大大的提高性能(其实是将多个段的等待时间重叠在一起了)
此时将数据进行批量发送。统一发送后,一起等待ACK的返回。这个批量传输的过程就是滑动窗口
等待的传输数据到达一定的数量,而这个批量等待的数据称为————》窗口大小
白色区域,相当于等待的窗口~~
批量发送了四个数据,就等待四个ACK。
这个批量发送也会出现问题。
比如:
- 数据包以抵达,而ACK却丢了
这种情况,即使丢了这么多的ack,对于可靠性也没有任何影响。
确认序号的意思是指,该序号之前的数据都已经收到了,后一个 ACK 能表示前面的 ACK。(覆盖了)
- 数据包就直接丢了
解释:由于刚才1001 - 2000 这个数据丢了,所以接收方任然要索要1001,不会说因为收到的是2001-3000,就返回3001。接下来几次的数据 ack ,确认序号都是1001, B 再次向 A 反复索要 1001 这个数据,A这边识别到多个 1001 的请求,就知道 1001-2000丢了。于是 A 就重传了 1001-2000 这个数据。
当 A 1001-2000 这个数据重传后,B 收到了就会 传一个7001这个ACK,因为,数据只是缺了1001-2000这一段,补上之后,不用传关于这个段的 ACK,根据滑动窗口特点,后一个 ACK 就能表示他前面的数据已经到达 B。也就是,虽然1001-2000这一段没收到,但是其他的还在收啊。只是没有应答。补上了之后就开始应答。
这个重传的过程也叫:快速重传。
五、流量控制(也是保证可靠性的机制)
按道理来说,窗口越大,意味着,批量传输数据越多,也就意味着整体速度越快。但是数据多,就不意味着安全可靠。
数据一次性发的多,而快,一下子就把接收缓冲区给充满了。如果继续发送数据,此时就会丢包。这个时候,就需要控制一下流量
当ACK为1的时候,窗口大小字段就会生效。这里16位窗口是建议。
重要:发送方的窗口大小 = 流量控制 + 拥塞控制
如此就达成了阻塞的效果。
六、阻塞控制
滑动窗口大小 = 流量控制 + 拥塞控制
流量控制:平衡了接收方的处理能力
阻塞控制:衡量了传输路劲的处理能力
在java网络编程的哪一章中网络的传输是需要经过很多个节点的。如果任何一个设备,处理能力达到瓶颈,都会对整体的传输塑料产生明显影响。
而阻塞控制,就需要找到,衡量中间节点,传输的能力。怎么找?通过一次次实验,找到一个合适的发送速率
- 开始的时候,按照一个小的速率发送
- 如果不丢包,就可以扩大窗口的大小
- 如果丢包,就可以缩小窗口的大小
慢开始:刚开始传输会给一个非常小的窗口
指数规律增长:每一次扩大窗口,是翻倍成长。快速接近网络传输路径的能力瓶颈。
传输轮次:第一次发送,第二次发送。。。。
拥塞避免,“加法增大” :指数增长懂啊一定的阈值,就变成线性增长。避免一次性增加很多突然超出上限。
网络拥塞:增长到一定程度,出现丢包,认为当前的窗口大小,已经是传输的极限了。
七、延迟应答(效率机制)
TCP 可靠性的核心,是确认应答,ACK要发,但是不是立即发,而是稍微磨蹭一会再发,TCP中决定传输效率的关键元素就是,窗口大小。
此时服务器并不会立即返回一个ACK,也不一定要等到服务器将缓冲区里的数据全部拿出来,等待一下,然后偷偷的拿数据,悄悄的消耗缓冲区的数据,这样就相当于增大了窗口的大小。然后等到合适的时机服务器在返回ACK应答。
这样使得窗口大小似乎变大了,然后效率相应的变大了。
但是也并不是所有的报都延迟。
- 数量限制:每隔N个包就应答一次;
- 时间限制:超过最大延迟时间就应答一次;
八、捎带应答(效率机制)
是基于延迟应答的。
适用于:客户端,服务器,之间的通信模型,通常是“一问一答”这用模式
通信模型:
- 一问一答:绝大部分服务器都是这样
- 多问一答:上传大文件
- 一问多答:下载大文件
- 多问多答:游戏串流
原则是,客户端发送一个消息,需要等待服务器的ACK,但是因为延迟等待,数据实际上已经上传了,并且也处理好了,按照一般逻辑就是,将处理好的数据,返回给客户端,之前 ACK 还没有返回给客户端,那么此时 ACK 就会捎带着服务器处理好的数据返回给客户端。(原本分两次的返回,现在一次就可以)
九、面向字节流(粘包问题)
这个有一个巨大的问题,就是粘包问题。
根据上面的说法综合一下就知道,我们发送方传输的数据,是多个数据放在一起传输的。然后在缓冲区上集结排列,然后接收方如何将数据分离,读出一个完整的数据报,此时造成的结果就是,容易读出半个包,或者一个半的包,反正就是不是我们想要的。
如何解决?
提前约定好,数据的格式。比如:
- 约定结尾符号,接收方收到后,通过结尾符号知道一个完整的数据。
- 约定长度,约定数据的前几个字节,表示整个数据包的长度,通过光标读取字节长度。读取完整数据。
十、异常情况(心跳包)
- 进程关闭 / 进程崩溃
- 进程没有了,socket是文件,随之关闭,虽然进程没有了,但是连接还在,仍然可以继续关闭连接(四次挥手)
- 主机关机
- 先杀死所有的用户进程,也会触发四次挥手,如果没有挥完,比如 对方发了一个fin,咱们没来得及 ack 就关机了,姿势对端,就会重传fin ,重传几次后,发现没有 ACK ,尝试重置连接,如果还不行,就直接释放链接。
- 主机掉电
主机瞬间关闭,来不及任何挥手操作。
- 对方是发送方 :接收方收不到ACK =》超时重传 =》重置链接 =》释放链接
- 对方是接收方:发送方,没办法立即知道,接收方死机了,这边还没有来得及发新数据,就没了。
- TCP为了防止这种情况,内置了一个机制,心跳机制(周期性的,如果没有心跳,就代表挂了),也就是接收方会给发送方定期发一个心跳包(ping),然后发送方会给接收方发送一个返回(pong)
- 如果每个ping都有一个pong返回,就说明接收方是好的。如果bing了很多次,还没反应,就知道就收方挂了。
- 网线断开
- 同上,与主机掉电原理一样