一:TCP
1:连接层管理
1:三次握手
1:意义
①: 三次握手,也是一种保证可靠性的机制."投石问路".TCP三次握手,就是要验证网络通信是否畅通,以及验证每个主机的发送和接收能力是否正常.恰好三次握手,就能验证双方的发送和接收能力是否正常.
为了帮助大家更好的理解,举个栗子:
在这个栗子中:麦克风:发送能力.耳机:接收能力====>通过三次握手,是进行可靠传输的前提条件.
2:三次握手的作用
三次握手,还能起到,"消息协商"这样的效果.
通信的时候会涉及到一些参数,需要双方保持一致.通过协商,来确定具体的参数是多少!!!
举个栗子:
你和你女朋友结婚的时候,办酒席,摆多少桌?
一场结婚酒席,包含两类人:婆家 娘家
2:断开连接
1:四次挥手
四次挥手示意图:
将由服务器发出的ACK和FIN合并成一项可以吗?或者将四次挥手可以改成三次可以吗?
四次挥手有的时候可以改成三次,有的时候不能改.
原因:
FIN的触发,是由应用程序代码控制的===>调用socket.close()来进行触发的,
如果close触发的慢,那么无法和内核控制的ack进行合并.那么,此时就是四次挥手
如果close触发的快,那么就可以和内核控制的ack进行合并.那么,此时就是三次挥手
即;两个步骤能否合并取决于close的执行快慢.
2:两个问题
a:如果服务器,始终不进行close,会怎么样?客户端的连接就始终不关闭吗?
客户端给服务器发送过去了FIN(在内核中完成),此时服务器收到了,在内核中触发了ack,ack为1,此时,服务器始终不进行close,会怎么样?客户端的连接始终不关闭吗?
原因:
情况1:
客户端给服务器发送FIN,服务器端的内核会立即向客户端发送ACK(在内核中完成),服务器端没有进行close操作,那么服务器端就会进入close_wait状态,当发送成功后,便会进入last_ack状态.
站在服务器的角度来看,此时,客户端已经删除了服务器端的信息,即使服务器不进行close操作,这个连接也不能使用了.
针对服务器端的socket进行读操作:
a:如果还没有读完(即缓冲区里面还有数据),可以正常读
b:如果已经读完了,由于TCP协议是面向字节流的,此时,(EOP为-1,hasNext为false)
针对服务器端的socket进行写操作:
无法进行写操作,此时便会直接报出异常.(客户端没有服务器端的连接,此时服务器端的socket进行写操作,将其当做响应传递给客户端,但没有办法找到响应的客户端,此时,便会抛出异常)
综上所述,此时这个连接就是废人一个.
情况2:
更极端的情况,close忘记写了,此时,客户端一直等待服务器的close,却一直没有等到,客户端便会单方面删除服务器端的连接)
b:如果通信过程中,出现丢包了,又怎样处理?
原因:
第一组的FIN/ACK丢失了,可以让其客户端进行重传
第二组的FIN/ACK丢失了:
如果第二组的ACK丢失了,让B进行重传FIN即可,但是当第二组的ack,即A向B发送ACK时,ack丢失了,此时B没有收到A的ack消息,B就会重新将FIN传递给A,(在此过程中,由于A将自己和B的连接给删除了),导致B的FIN传不到A里面去,那么,B就永远也收不到A的ack了
为了解决这一问题,A在发送出去最后一个ACK时,便会等一会(A的等待时间,网络上任意两点之间传输时间的最大时间*2),万一B没有收到,此时就可以等B再重新发送FIN ,直到B接收到由A发送的应答报文(ack).这样,A就可以释放连接了.
MSL:存活时间,理论啊上拍脑门拍出来的时间,这个时间是s/ms
3:TCP是如何可靠传输的?
1:确认应答=====>可靠性传输的核心
2:超时重传
3:连接管理(三次握手,四次挥手)
4:滑动窗口
提高传输效率(更正确的说,是让TCP在可靠传输的前提下,效率不要太拉胯)
使用滑动窗口,不能使TCP变的比UDP快,只能说缩小两个的速度差距
无滑动窗口:将大量的时间用在等待ack上
有滑动窗口:"一次等待时间"等三个ack.
注意:
1:窗口越大,批量发送的数据也就越大,运行效率更高
2:窗口也不能无限大,相当于批量发送的数据无限多,此时,便会接近udp协议,所以窗口不能无限大另一方面,如果窗口无限大,接收方能不能处理过来,中间的设备能不能承受的住!!!都是未知数.
滑动窗口图例:
如上图:主机A给主机B发送数据,1001-5000的数据
数据如下:
1001-2000 2001-3000 3001-4000 4001-5000
当B接收到A的数据后,B就会给A返回上述四组数据所对应的ACK.
当A收到2001的ack之后,A就会向B传输5001-6000这个数据,
A向B发送的数据就会变成如下的样子:
2001-3000 3001-4000 4001-5000 5001-6000
我们会发现,当一个ack传完之后,会将已经传完的ack进行过滤掉,往后再移动一个格子.类似于滑动一样.
滑动窗口批量传输数据时,发送丢包,该怎么办?
情况1:数据包丢了
一旦丢失的数据报被补上了,此时,1001-2000后面的数据都不必重新传输了(都在缓冲区待着呢)====>此时,就完成了重传的过程.这个过程,只是把丢失的数据进行重传了===>快速重传(超时重传+滑动窗口)
情况2:ack丢了
如上图所示:ack丢了,不用做任何处理!!!
原因:
1001的ack丢了,但2001的ack传递过去了,在前面我们学习的过程中.2001代表2001之前的数据都被传递过去了即包含(1-2000)的数据,换句话说,包含1001的ack.
5:流量机制
流量控制:实际上为了给滑动窗口的窗口大小做限制(如果滑动窗口窗口过大,会导致接收方接收不过来,中间链路处理不过来)
以上图为例:
主机A 给主机B的内核态发送数据,内核将发送的数据存储在接收缓冲区.
假设:A生产速度很快,B这边的消费速度跟不上,那么,就会导致接收缓冲区的数据越来越多, 最终就满了,此时,剩余的传过来的数据便会被丢包
流量控制怎样解决?
流量控制会根据接收方的内核中的接收缓冲区剩余大小作为主机A给主机B传输数据的速度大小的依据,当接收缓冲区剩余的空间越大,那么,应用程序消费的数据也就越大.同时主机B便会将剩余缓冲区的空间当做ack发送给主机A,作为发送方下一次的数据,窗口大小.
如上图所示:流量控制可以控制滑动的大小.
假设滑动窗口默认是4000,刚开始主机A已经给主机B发送了1000大小的数据,相当于剩下3000的接收缓冲区.那么,此时主机A连续主机B发了3000大小的数据,此时,主机B还没有来得急处理从主机A的3000大小的数据,但此时主机B的接收缓冲区大小已经满了.相当于剩余缓冲区为0.
主机B就不会接收从主机A传来的消息,即主机A先暂停发送(ack=0).主机A便会发送一个"窗口探测包",只是为了触发ack(查询当前缓冲区的情况,一旦发现ack=1)时,便可以继续发送.
接收方就可以根据窗口大小,来反向限制发送方传输速度了.