赞
踩
码字不易,转载请附原链,搬砖繁忙回复不及时见谅,技术交流请加QQ群:909211071
报文段首部格式一定要记住,至少要大概理解后,再带着疑问去看下边,否则看下边有可能会一脸懵逼。
TCP的可靠传输通过滑动窗口实现,滑动窗口以字节为单位。凡是已发送过的数据,在未收到确认之前都必须暂时保留,以便在超时重传时使用,窗口越大,发送方就可以在收到对方确认之前连续发送更多的数据,获得更高的传输速率。滑动窗口可以分为四部分:
窗口依赖三个指针,P1,P2和P3,小于1的是已经发送并收到确认的字节,1和2之间的是已发送但未收到确认的字节,2和3之间是允许发送但尚未发送但字节,大于3为不允许发送的字节,1和3之间的字节数即为接收方规定的滑动窗口大小(流量控制)。接收方B只能对按需收到的最高序号给出确认,当指针2和指针3重合时,发送方必须停止发送数据,等待接收方的确认,为了保证可靠传输,A必须等到B的确认才能继续发送数据,为了保证发送方不会一直等待,到达依赖超时计时器(动态计算超时重传时间)规定的时间后,就重传这部分数据,直到收到接收方B的确认。
超时重传时间的选择采用一个自适应算法,它会记录一个报文段发出的时间,以及收到响应的确认的时间,作为往返时间RTT,计算为加权平均往返时间RTTs,RTTd为RTT的偏差的加权平均值,通过这几个值计算得到超时重传时间RTO。计算的规则是:报文段每重传一次,就把超时重传时间RTO增大一些,当不再发生报文段的重传时,根据下面公式计算RTO。
RTO = RTTs + 4 * RTTd
流量控制就是让发送方的发送速率不要太快,要让接收方来得及接收,利用滑动窗口实现。发送方的发送窗口不能超过接收方给出的接收窗口大小。当接收方接收能力下降时,会缩小自己的接收窗口,发送方通过报文头部的串口字段告知发送方。
还以上面发送方为A,接收方为B作为例子,当B的接收缓存满之后,将自己的接收窗口置为0,通过报文的窗口字段告知发送方A,过了一段时间后,B的应用程序将接收缓冲区的数据处理了一部分,接收窗口又有了空间,假设为300,此时B向A发送报文告知对方接收窗口为300,如果该报文在传输中丢失了,A不会一直傻傻地等待,在A收到B的0窗口报文后,就会开启该连接设定的持续计时器,当超过计时器规定的时间后,A会向B发送一个0窗口探测报文段,B在收到这个探测报文后,会重新发送300的窗口报文段。此时有人会问,B的接收窗口大小为0了,怎么还能接收0窗口探测报文段?这里需要记住:TCP规定,即使接收窗口为0,也必须可以接收0窗口探测报文段、ACK=1的报文段和URG=1的报文段
TCP进行拥塞控制的算法有4个:慢开始、拥塞避免、快重传、快恢复。
下面通过一道题来理解:
问题:设tcp的拥塞窗口的慢开始门限值初始为8,当拥塞窗口上升到12时网络发生超时,那么第13次传输时的拥塞窗口大小为多少?
解答:
最开始的 Client 和 Server 都是处于 Closed,由于服务器端不知道要跟谁建立连接,所以其只能被动打开,然后监听端口,此时 Server 处于 Listen 状态;
而 Client 会主动打开,然后构建好 TCB 「SYN= 1,seq = x」,发送给服务器端,此时 Client 会将状态置为 SYN_SEND 「同步已发送」;
服务器端收到客户端发来的同步请求后,会将状态置为 SYN_RECV「同步已接收」,同时会构建好 TCB「SYN = 1,seq = y,ACK = 1,ack = x + 1」发送给客户端;
客户端接收到了服务器端发来的传输控制块之后,会将自己的状态改为 ESTABLISHED「建立连接」,然后发送确认报文(ACK= 1,seq = x + 1,ack = y + 1);
服务器端在收到了客户端发来的报文之后,也将状态置为 ESTABLISHED「建立连接」,至此,三次握手结束,当然在这里,可以带 tcp 报文段信息过来了,因为此时客户端已经可以保证是可靠的传输了,所以在这一端可以发送报文段了。
题目1:为什么A最后还要再发送一次确认?
解答:为了防止因为网络原因迟来的A连接请求到达B,B单方面建立连接浪费B的资源。假如A发送了连接请求,但未收到确认,于是A又重传了一次连接请求,第二个连接请求正常连接并传输数据。此时A的第一个连接没有丢失,只是延迟了,又到达了B,B误以为是A又发送的一次连接,假如此时B向A发出确认后,A会丢弃B的确认,假如没有A最后一次的确认,B就会在发送确认后建立连接,白白浪费B的资源。
题目2:为何不直接在第一次握手就带上报文段消息,非要第三次才可以带?
解答:因为 TCP 是要保证数据的不丢失且可靠,如果在第一次就带上报文段消息,此次建立连接很有可能就会失败,那么就不能保证数据的不丢失了,在不可靠的机制上进行这种操作,换来的代价太大,每次发送报文段的资源也会增大,得不偿失;而第三次握手的时候,客户端已经知道服务器端准备好了,所以只要告诉服务器端自己准备好了就ok了,所以此时带上报文段信息没有任何问题。
最开始客户端和服务器端都是 ESTABLISHED 的状态,然后客户端会主动关闭,而服务器端则是被动关闭。(主动关闭的我们认为是客户端)
客户端发送一个 FIN 报文段,seq = 结束的报文段序号 + 1「假设为 u」,告诉服务器端,客户端需要关闭了,此时将客户端的状态变为 FIN-WAIT-1,等待服务器端的反馈;
服务器在接收到了客户端发来的 FIN 包之后,会发一条 ack报文反馈给客户端,其中报文中包括 ACK = 1,seq = v,ack = u+1,告诉客户端收到了客户端要关闭的消息了,同时服务器端会通知应用进程需要关闭连接了,并将自己的状态置为 CLOSE-WAIT;
由于服务器端可能还有一些数据没处理完,所以需要一段时间的等待,当处理完了之后,会再发一条报文,其中 FIN = 1,ACK = 1,seq = w,ack = u+1,告知客户端,服务器端现在可以关闭了,并将服务器端的状态由 CLOSE-WAIT 变为 LAST-ACK;
客户端在收到了服务器端发来的消息之后,会发一条ack报文「ACK = 1,seq = u+1,ack = w+1」回去,告知服务器端,客户端已经知道了你准备好关闭了,此时会将客户端的状态由 FIN-WAIT-2 置为 TIME-WAIT,在两个最长报文段传输时间过后,会自动将客户端的状态由 TIME-WAIT 置为 CLOSED。
服务器端收到消息之后,就将状态由 LAST-ACK 置为了 CLOSED,自此,四次挥手全部结束。
题目1:在B向A发送FIN断开连接的报文后,A发送确认后做了什么?是马上断开吗?为什么要等待2个时间周期?
解答:图里很明了,并不是马上断开,而是等待了2个时间周期。原因有2个:
题目2:主动关闭方和被动关闭方,谁先进入CLOSE状态?
解答:看图也可以发现,是被动关闭方先进入CLOSE状态,原因上一题的答案就可以解释明白。
题目3:为何不能三次挥手呢?
解答:首先如果去掉最后一次挥手,那么服务器端就不知道自己要关闭的报文有没有传输成功,可能半路上就失败了,但是此时客户端不知道,导致客户端一直在等待服务器关闭,但是此时服务器端直接就关闭了。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。