0


网络编程(5)-----javaweb

面试题:谈谈对三步握手协议的理解和四步挥手协议的理解

3连接管理(面试常考)

3.1 三步握手协议(建立好连接,随时进行通信)

TCP是有连接的,连接管理指的是如何建立连接(三步握手协议),如何断开连接(四步挥手)协议,

1)A 发送一个请求,尝试和B建立连接(同步报文段请求连接syn)

2) B 确认A的请求,并尝试和A建立连接(ack+syn)

3)A 确认连接(确认报文段ack)

上图是TCP的保留6位;它的作用是标识TCP数据报是一个啥样的数据包,其实中间的的两次操作,是可以连在一起的;当A的syn连接到B后,B的内核会在第一时间发出响应****,就会立即应答,B会发出syn(尝试建立连接)给A,同时会立即向A发出ack(确认刚才已经收到A发送出去的请求),这两件事是同时处发的,就没有必要进行四部握手协议;

为什么要进行三步握手协议?为什么要建立连接?

1 投石问路:通过三步握手协议,来进行确定A和B之间的传输是否是通畅的,尤其是确认A和B的发送能力和接受能力是否是正常的;

2协商参数:通过三次握手,让A和B之间通通气,选择一些合适的参数,例如TCP的序号从几开始;

我们来举一个例子吧:例如在一个打电话情境中

TCP中一些重要的状态:1)LISTEN:手机开机,信号良好,随时可以进行打电话,当我们创建好了serversocket的时候,就进入了这个状态; 2)ESTABLISHED:接通了电话双方可以说话了,代码中accept返回,得到了一个ClimentSocket

四部握手可以吗? 可以,但是没有什么必要,这样比较麻烦,要对多余的一次数据进行封装和拆装;

3.2四次挥手协议

客户端和服务器都可以主动断开连接**;**

** 1.我是这么理解的:FIN相当于请求尝试断开连接,ACK相当于尝试确认已经断开连接;但是为 什么此时的ACK和FIN不放到一起发送呢** ?

** 对于B来说:ACK的触发和FIN的触发实际上是不一样的,B只要收到A发出的FIN就会立即触发ACK; 但是啥时候发送FIN是用户自己来决定,只要代码中出现了socket.close()这样的操作的时候,才会触发FIN;**

** 但是实际上FIN的触发,本质上是内核里面释放了对应的PCB的文件描述符,比如你虽然没有调用socket.close(),但是被GC回收了,或者进程结束了,PCB都要被销毁,PCB的文件描述符表没了,进一步的文件描述符也就没了,这同样会触发FIN; **

1)ClLOSE_WAIT:服务器收到了FIN进入的状态,等到用户调用close,来发送FIN;

** 2)TIME_WAIT:表示客户端收到了FIN之后,进入了这个状态,这个状态主要的目的是处理最后一 次ACK丢包;假设如果A收到FIN的时候,不进入到这个状态,立即销毁断开连接,就会造成如果丢包了,就再也发送不了ACK了;**

第一个FIN丢了,A迟迟收不到ACK,就会尝试重传FIN; 第一个ACK丢了,A迟迟收不到ACK,就会重传FIN; 第二个FIN丢了,B迟迟收不到ACK,就会重传FIN; 第二个ACK丢了,B迟迟收不到ACK,还是会重传FIN;

TIME_WAIT即使在进程销毁以后,这种状态仍然会存在,知道一定时间后没有重传的FIN过来后才会销毁;TCP连接不会立即进行销毁;而是会等待一定的时间,一般是等待的时间为:主机A和主机B曾经尝试最长的通信时间*2;在linux中,默认时间是1min,这个都是可以配置的

如果在调试代码的过程中出现了大量的CLOSE_WAIT,这是说明socket.close()没有被及时地调用到;

哪方先断开连接,就会先进入TIME_WAIT,进程退出后,这个状态还会存在,TCP也不会断开连接;如果服务器进入了这个状态,原来的连接还在占用这个端口,这时如果服务器立即启动,新的进程又会再次尝试绑定这个端口,这时就可能会出现端口绑定失败的情况,怎么解决呢?

先结束客户端,在结束服务器;如果使用原生的socket,就可能会失败,但是在javaSocket,第二次启动也可以成功的,在SocketAPI中,有了REUSEAE_ADDP选项,如果把这个选项加上,就可以在绑定端口的时候复用TIME_WAIT状态中的端口,在JAVAsocket中就默认了这个选项;

四次挥手必须是四次吗?可以是三次吗? 有,延时应答和捎带应答(会将ACK和FIN是有可能合并在一起的)

四次挥手一定会执行吗? 不一定,四次挥手是一个正常TCP断开的流程,但实际上有的情况TCP也会异常断开;例如网线都断了! ——————————————————————————————————————————— 上面的几种机制都是为了保证可靠性,但是TCP也要尽可能地保证效率,本质上来说,保证效率和保证可靠性是比较冲突的;要等待接收ack,这是需要时间的,但是TCP在保证可靠性的前提下,努力的提升传输效率,提升性能;

4 滑动窗口

ACK:他是确认应答机制里面的,接收者告诉发送者自己受到的那些数据,里面有序列号

例如A向B发送了1-1000的数据,B会告诉A我收到了1-1000,B在向A索要1001-2000的数据

现在咱们批量发送一次发一波,一次等一波的ACK,这是就把等待多组ACK的时间重叠起来了

窗口大小:一次批量发的数据的长度,称为窗口大小;如果没有批量发送数据的长度限制,其实就没有可靠性而言,窗口越大整体的效率就会越高,窗口越小整体的效率就会越低

当前窗口范围就是1001-5000,也就意味着发送方现在同时发送了1001-2000,2001-3000,3001-4000,4001-5000;同时还在等待4组数据的ACK,假设2001这个ACK先到,发送方也就知道1001-2000的数据已经收到了,发送方也就不用再等这个数据;直接将窗口向后移一位,发送5001-6000的数据,仍然保证当前窗口是四份数据

这时依然在等待四份数据的ACK,并不是在等待四份数据的ACK都到达了,才发新的数据,而是随时受到ACK,就随着向后发送;

**这时传输ACK会存在后发先致的情况 **

ACK 2001,3001,4001,5001都在网络上运输呢不一定非得是2001先到呀,可能是3001,4001先到呀 这样也没有关系;确认序号表示,从该序号之前,前面的数据已经接收到了,如果收到了4001这个ACK,说明1001-2000,2001-3000,3001-4000的数据已经收到了,此时前面的这几组ACK收或不收已经不关键了;

滑动窗口的丢包问题:

情况一:数据包已经抵达,ACK却丢了 这种情况不打紧,只要不是全丢了就好,发送方如果1001这个ACK丢了,但是3001这个ACK到了,这事也就知道了3001之前的数据应该都到了;此时前面的数据已经接收到了;实际上有时TCP为了偷懒,滑动窗口下并不是每条数据都有ACK,有时会个几条才会发一个ACK;

情况二:直接发的数据就丢了

这时1001-2000发的数据包丢了,因为1001丢包了,主机B返回的ACK就会一直索要1001,确认信号就一直是是1001,1001-2000丢了,B就会给A返回一个ACK,由于包丢了,就会一直返回(我收到0-1000的数据,我想要1001之后的数据)

1 A向B发送了1-1000的数据,B返回一个ACK,表示收到了1-1000的数据,还向A索要1001之后的数据

2 A发送1001-2000的数据,B没反应

3 A在发送2001-3000的数据,B在不丢包的情况下会返回一个我已经接收到了2001-3000的数据,确认序号是3001的ACK;但是此时B丢包了,返回的ACK就变成了我已经收到了0-1000的数据,我索要1001-2000的数据;这时A还会发送2001-3000的数据,B确实收到数据了,但返回的ACK和上面是一样的;这时A还会发送3001-4000的数据,但此时B返回的ACK仍然是向A索要1001-2000的数据;(2001-4000的数据已经让B存放在接收缓冲区里面了)

**这时A就知道自己已经丢包了,就会给B再次发送1001-2000的数据,此时B收到了0-4000的数据,这时返回的ACK就是我已经收到了0-4000的数据,就会再次向A索要4001之后的数据了 ————————————————————————————————————————— 5 流量控制:他这个机制的本质目的是在控制滑动窗口的大小,她是想要保证可靠性的,窗口的大小决定了传输的效率,窗口越大,效率就会越高; **

窗口越大,资源开销就要更多,窗口越小,效率就得不到保证;

此时的流量控制就是基于接收方的一个处理数据的能力来限制窗口大小的

TCP这个数据的传输过程类似于生产者,消费者模型

主机A在给B发送数据到达了B的接收缓冲区,此时主机A就是生产者,B的应用程序通过SocketAPI来读取数据,被SocketAPI读取得数据 就从缓冲区删掉了,此时应用程序就是消费者,接收缓冲区就相当于消费场所

此时说的窗口大小,就是批量发送的数据有多少,例如主机A发的数据很多,窗口很大,此时接收缓冲区的数据也会增长很快。如果主机B读的数据很慢,随着时间的推移,接收缓冲区逐渐满了,如果此时不加任何限制,主机A还是按照原来的速度发,这时新来的数据没有地方保存了,就被内核丢了;

流量控制这件事情,就是根据接收方的处理能力(接收缓冲区的空余于空间大小),来动态决定发送方的发送速率(滑动窗口的大小)

例如,接收缓冲区的大小现在是4000,当1-1000的数据到达的时候,缓冲区这里用了1000,还剩3000,返回的ACK就会把这个信息发回去,发送方再次发送数据的时候,就会按照3000这个滑动窗口来发送数据;

如果窗口大小变成0了(接收缓冲区已经满了),这是发送方就啥也不干了么?

不是的,发送方此时是不会再发送数据了,但是为了可以查询当前接收方的窗口大小每隔一段时间还会发一个窗口检测包,通过这个包(不会传输具体的业务),触发ACK,现在就知道当前窗口的大小了;

TCP是传输层协议,在TCP首部40字节选项中包含了一个窗口扩大因子 M,实际上的窗口大小是窗口字段的值左移M位

标签: java-ee

本文转载自: https://blog.csdn.net/weixin_61518137/article/details/123791858
版权归原作者 小比特大梦想 所有, 如有侵权,请联系我们删除。

“网络编程(5)-----javaweb”的评论:

还没有评论