网络协议的在实际运用是分为5层协议及:
这五层结构在,java 网络编程中已经有所现,具体用法具体实现的功能,都有。
这里主要的一个协议也是目前网络上最常用的一个协议,HTTP协议。
这层结构,决定数据要传输什么,拿到数据后如何使用。HTTP为什么是最长用的一个应用层协议,其本质就是在确定框架后,程序员可以在自定义一些协议,可控性和操控大大提升。
及约定数据报的数据格式,就是在自定义协议。
而如何约定?
网络上传输的,本质都是二进制字符串,就需要将上述的穿输的信息整合为一个字符串。但是在传输内容的时候,一个我们需要的数据,其实和其他数据是合在一起的。我们要如何拿到所需要的数据。很简单,我们在传输数据的时候,设定一个符号,或者距离单位。锁定所需要的数据。
比如我规定,属性之间用 ’,‘ 隔开 ,每个对象用 ‘\n’ 隔开,结束标志用 ’;‘ 隔开 。
只要发送方发送数据按照这个格式传输,然后接收方,在解析数据的时候,用这个格式解析就好;
在开发中,有一些特定的现成的格式。可以直接拿来使用。比如之前的一种典型的格式,xml ,还有现在用的比较多的一种格式。json,
什么是json。
{
userId:100
userPos:10-100
}
使用{}作为标识,{}里面的诺干个键值对,每个键值对用 ’,‘ ,分割,键值对,用 ’:‘ 分割。 键必须是字符串,值就可以是一个object。
TCP如何实现可靠传输?
什么是确认应答呢?其实就很简单,比如网上购物,商家发货,货物根据你的信息发送货物,然后送到哪里并且,你确认了收货,然后平台就会将钱给商家,这就是一个很简单的应答模式。
这个模型有一个问题,就是网络的状况有很多种,会出现后发先至的问题。对应到上述的例子就是,遇到强降雨,但是后边的货车走了另一条路。走的就比之前的车要快。
异常状态
正常状态
如何解决这个问题?
此时就需要对消息进行编号。
协议格式:
TCP将每个字节的数据都进行了编号,基序列号。
首先接送方的序号和发送方的序号无关
确认序号 1001 的含义。
在之前说过,网络传输的时候,会有后发先至的情况,同样的这个也是。但是能,TCP在传输时会有序号,序号天然就有顺序,所以对于这种情况,只要在接收方接收前排序就好了。(优先级队列就能完成这个)、
对于TCP来说,自身也承担了这个整队的任务,TCP会有一个缓冲区(内核中的一个区域),每个socket都有自己的缓冲区。然后TCP就可以按照序号针对收到的消息进行针对。
如果一切顺利就可以应答了。可问题就是,现实并不顺利。比如丢包。
那么什么是丢包呢?
传输数据的时候要经过各个节点,可是问题来了,一台机器节点的转发能力是有极限的,也就是说到达极限的机器,就可能会引起丢包的问题。(实时性的APP或者应用对于丢包的问题非常的敏感),如果丢包了接收方就收不到了。自然就不会返回ACK。
如何解决?
之前的逻辑,就是发送方发送消息会受到一个接收者的反馈的接收信息。但是丢包了,数据就不完整了。在队列里之前排好序的数据,因为丢包使得,序列并不完整。
此时因为序列并不完整,就不会交给接收者。然后序列就会等待,如果此时的序列等待时间长了,发送方迟迟拿不到应答。那么发送方就知道,传输出了问题,那么就会重新再发一次。直到拿到应答。(这个就叫超时重传)
有一个细节:
此时发送方分不清这些情况,只能重传。
可是此时,接收方已经在缓冲区中有了数据,再传一个,就重复了。此时接收方的缓冲区,会根据数据的序列,自动去重。保证应用程序中读到的数据任然只有一份
如果多次重复发送还没有收到ack 那么多半网络出了问题。主要是假设丢包率为10%,那么连续两次丢包,概率就是10% * 10%=1%
而以上这两种机制就是,TCP的可靠性保障。及:
TCP创建链接:三次握手
TCP断开链接:四次挥手
什么是三次握手?
握手是指通信双方,进行一次网络交互。相当于客户端和服务器之间,通过三次交互,建立连接关系(双方各自记录了对方的信息)
互相应答,确保双方的网络是完整的。并建立连接(有那么一滴滴可靠性的说法,但并不是)
等建立连接完毕,服务器 accept 把建立好的链接从内核拿到应用程序中。
做这么多。其实就是验证自己的发送能力和接收能力是否正常。
什么是四次挥手?
通信的双方,各自给对方发一个FIN(结束报文),在各自给对方放回ACK
建立连接,一定是客户端发起,但是断开链接客户端和服务器都有可能。(并且通常ACK与FIN并不能重合)(FIN比特位位1的时候)
注意:
三次握手:ack与syn是同一个时机触发的(都是由内核完成)
四次握手:ack与fin内是不同时机出发的。前者由内核完成,而后者需要程序中 socke t的 close 方法才会触发fin、
是为了解决,数据传输的效率问题。
对每一个发送的数据段,都要给一个ACK确认应答。收到ACK后再发送下一个数据段。这样做有一个比较大的缺点,就是性能较差。尤其是数据往返的时间较长的时候。
可靠性的提升,往往代表,效率的损失。
可以看到,客户端A这边传输5000个字节,要应答四次。此时A就用了大量的时间去等待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应答。
这样使得窗口大小似乎变大了,然后效率相应的变大了。
但是也并不是所有的报都延迟。
是基于延迟应答的。
适用于:客户端,服务器,之间的通信模型,通常是“一问一答”这用模式
通信模型:
原则是,客户端发送一个消息,需要等待服务器的ACK,但是因为延迟等待,数据实际上已经上传了,并且也处理好了,按照一般逻辑就是,将处理好的数据,返回给客户端,之前 ACK 还没有返回给客户端,那么此时 ACK 就会捎带着服务器处理好的数据返回给客户端。(原本分两次的返回,现在一次就可以)
这个有一个巨大的问题,就是粘包问题。
根据上面的说法综合一下就知道,我们发送方传输的数据,是多个数据放在一起传输的。然后在缓冲区上集结排列,然后接收方如何将数据分离,读出一个完整的数据报,此时造成的结果就是,容易读出半个包,或者一个半的包,反正就是不是我们想要的。
如何解决?
提前约定好,数据的格式。比如: