传输层——TCP协议(2)

上一篇:http://t.csdnimg.cn/DwNLQ

 滑动窗口

确认应答策略对每一个发送的数据段, 都要给一个ACK确认应答,收到ACK后再发送下一个数据段.
这样做有一个比较大的缺点, 就是性能较差. 尤其是数据往返的时间较长的时候.

因此为了提高发送效率,发送端一次发送大批的报文。

对于已经发出去但还没有收到确认的报文,要被tcp暂时保存起来。

 这样的话,已经发出但还没收到确认的报文,在发送端存在多个。

发送的数据本来就在发送缓冲区,没有必要在把这部分数据另外保存;

  • 发送缓冲区分为三部分(方向固定从左向右):已发送已确认的数据,已发送未确认的数据,未发送未确认的数据,已发送已确认的数据可被覆盖,那就意味着这部分的数据被TCP移除了。
  • 已发送未确认的部分,暂时被保存在发送缓冲区,同时这部分窗口也被称作滑动缓冲区,最大范围为对方接收窗口的大小
  • 所谓的区域划分,就是通过指针或者数字下标来区分就行。具体是只需要设置两个指针,一个指向窗口的开始,一个指向窗口的结束,只要把指针向右移,那就是窗口滑动

 因为有滑动窗口的存在,发送端才能一次发送大量的数据。

确认序号意味着确认序号x之前的数据全部接收,保证了滑动窗口线性的连续的更新,不会出现跳跃的情况(不会出现序号2000丢了确认序号调到3001)

  • 如果丢包了,怎么理解滑动窗口
  • 快重传机制:当发送端同时发送一大批数据时,如果丢包,接收端会收到三个同样的确认应答时立即重发报文
  • 超时重传机制:如果丢包没有触发快重传机制,静等超时重传

快重传机制是最高效率,超时重传是兜底(双重策略)

  • 滑动窗口怎么滑动,大小会变化吗,会变成0吗

滑动窗口不会向左移动,会向右移动

移动的时候,窗口大小会动态变化(会变大,变小,不变),如果接收缓冲区为0滑动窗口也会变为0.

  •  滑动窗口会越界吗

不会,tcp在逻辑上采用了类似环状算法,已发送已确认的数据可以直接覆盖

流量控制是通过滑动窗口实现的。

为了防止网络中的游离报文与起始序号发生重复,发送端和接收端在三次握手时不仅协商了双方缓冲区大小和建立连接,同时也协商了随机序号。

因此,确认序号-协商随机序号=下次发送数据在缓冲区的位置

流量控制

接收端处理数据的速度是有限的. 如果发送端发的太快, 导致接收端的缓冲区被打满, 这个时候如果发送端继续发送,就会造成丢包, 继而引起丢包重传等等一系列连锁反应.因此TCP支持根据接收端的处理能力, 来决定发送端的发送速度. 这个机制就叫做流量控制(Flow Control);
 

  • 第一次发送时,怎么保证发送量是合理的

发送端和接收端在通信前,要进行三次握手。三次握手中,双方已经交换了报文,已经协商了双方的接受能力。第三次的时候,可以携带数据(捎带应答)。

  • 流量控制属于可靠性(防止正常丢包),也提高了效率。
  • 接收端将自己可以接收的缓冲区大小放入 TCP 首部中的 "窗口大小" 字段, 通过ACK端通知发送端;
  • 窗口大小字段越大, 说明网络的吞吐量越高;
  • 接收端一旦发现自己的缓冲区快满了, 就会将窗口大小设置成一个更小的值通知给发送端;
  • 发送端接受到这个窗口之后, 就会减慢自己的发送速度;
  • 如果接收端缓冲区满了, 就会将窗口置为0; 这时发送方不再发送数据, 但是需要定期发送一个窗口探测数据段, 使接收端把窗口大小告诉发送端.接收端缓冲区数据如果被取走了,也会发送一个窗口更新通知给发送方。

延迟应答

 客户端和服务器都有自己的发送和接收缓冲区,都有自己的滑动缓冲区,而发送方一次发送更多数据,发送的效率就越高;而发送方要想发更多数据,在于接收方能接受多少数据,有多大的窗口。因此接收方要如何通告发送方一个更大的窗口呢?

接收方收到一个报文后不着急给对方应答,先等一段时间(不超过规定时间),这样上层有较充分的时间来取走数据,接收缓冲区的空间就比较大,这叫延迟应答

窗口越大, 网络吞吐量就越大, 传输效率就越高. 我们的目标是在保证网络不拥塞的情况下尽量提高传输效率;

  • 那么所有的包都可以延迟应答么? 也不是

数量限制: 每隔N个包就应答一次;

时间限制: 超过最大延迟时间就应答一次;

具体的数量和超时时间, 依操作系统不同也有差异; 一般N取2, 超时时间取200ms;

捎带应答

TCP提高效率的一种机制。就好比一下:

而这时ACK就可以跟I am fine,thank you,and you一起捎带应答,搭顺风车。

 

 连接管理机制

在客户端和服务器进行通信前,要先进行建立连接,也就是要进行三次握手

三次握手

要建立连接,那么客户端就要向服务器发送设置了SYN(请求建立连接)的TCP报头,客户端的状态就变成了SYN_SENT,而服务器收到后,就会发送一个包含ACK(确认收到)和SYN的TCP报头,服务器的状态变成了SYN_RCVD,随后客户端发送ACK的TCP报头给服务器收到后,客户端和服务器的状态都变成了ESTABLISHED,至此三次握手就建立成功了。

 但是客户端给服务器发送消息并不一定都是成功的,TCP虽然保证可靠性,但是也允许连接建立失败,因为TCP有超时重传机制。而且客户端发消息给服务器和服务器收到消息之间是有时间差的,客户端在三次握手中把第三次的报文发送出后,客户端就觉得连接已经建立成功了,而服务器此时并不觉得建立成功,如果第三次报文在途中丢失,那么服务器就认为连接没有建立成功,此时客户端和服务器对于连接建立的观点不一致,因为第三次报文发出后是没有确认应答的,所以当客户端发送数据时服务器就会发送一个 带有RST(对方要求重新建立连接)的TCP报文来提醒客户端连接没有建立成功,要重新建立。建立成功后,双方进行正常通信。

  • 为什么要有三次握手
  1. 三次握手本质是四次握手,中间第二次发送的是捎带应答,客户端给服务器发请求,服务器必须无条件答应
  2. 客户端和服务器在进行三次握手都至少发送和接收一次,这验证双方可以可靠的进行全双工通信
  3. 奇数次握手,确保一般情况下失败的话,把握手失败的连接成本嫁接到了客户端,可以保证服务器的稳定性

双方在通信完后,就会互相协商断开连接,因此就会进行四次挥手。

四次挥手

  1. 在应用层断开连接,就会调用close来关闭;而在传输层,则是客户端向服务器发送FIN的TCP报头,客户端的状态变成了FIN_WAIT1,而服务器会发送ACK的TCP报头来进行确认应答,服务器的状态变成了CLOSE_WAIT,当客户端收到确认应答后状态变成了FIN_WAIT2,这是两次挥手。
  2. 断开连接的本质是双方都没有数据给对方发送了。所以必须断开两次。
  3. 如果服务器也没有数据发送给客户端,那他也会发送FIN的TCP报头给客户端,状态变成LAST_ACK,客户端也会发送ACK来确认应答,状态变成TIME_WAIT,TIME_WAIT持续存在2MSL的时间后变成CLOSED,服务器收到应答后状态变成CLOSED。这就是完整的四次挥手。
  •  为什么是TIME_WAIT的时间是2MSL
  1. MSL是TCP报文的最大生存时间, 因此TIME_WAIT持续存在2MSL的话就能保证在两个传输方向上的尚未被接收或迟到的报文段都已经消失(否则服务器立刻重启, 可能会收到来自上一个进程的迟到的数据, 但是这种数据很可能是错误的);
  2. 同时也是在理论上保证最后一个报文可靠到达(假设最后一个ACK丢失, 那么服务器会再重发一个FIN. 这时虽然客户端的进程不在了, 但是TCP连接还在, 仍然可以重发LAST_ACK);

这也印证了TCP通信是基于连接的。

  • 为什么要四次挥手

客户端想跟服务器断开连接,服务器确认应答;但由于服务器还有消息要发送给客户端,所以他不断开连接,一来一回就是四次。如果有巧合(客户端断开时服务器也想断开),那么就可以捎带应答了。

  •  保证TCP的可靠性:

校验和,序号,确认应答,超时重传,连接管理,流量控制,拥塞控制

  • 保证TCP的高效性:

滑动窗口,快重传,延迟应答,捎带应答

以上的策略,除了拥塞控制,都是在客户端和服务器(两端机器)上进行的,而拥塞控制,则是为了网络信道情况考虑的。

拥塞控制

如果发送数据,出现问题,可能不仅仅是对方主机出现问题,还存在网络出现问题。

如果通信出现少量丢包,这在网络中是很正常的现象;如果出现大量丢包,那大概率是网络的问题,可能是硬件出现问题,也可能是网络数据量太大,引起阻塞。

因此,如果通信双方出现大量数据丢包(大量数据超时),那TCP就会考虑是网络出了问题。

  • 那么发送方应该怎么做?

不能立即对报文进行超时重传,首先不清楚网络哪里出了问题,其次贸然重发了的话也只会加重阻塞现象。

网络不止一个人用,大家都是使用TCP/IP协议,资源是共享的,一旦出现问题,大家都不会重发,这也是共识。每台识别网络阻塞的主机,都要进行拥塞控制。

TCP引入 慢启动 机制, 先发少量的数据, 探探路, 摸清当前的网络拥堵状态, 再决定按照多大的速度传输数据;

此处引入一个概念程为拥塞窗口(主机判断网络健康程度的指标)

  1. 如果发送数据量超过拥塞窗口,会引发网络拥塞,否则不会
  2. 网络是动态的,拥塞窗口的大小也会是动态变化的

发送开始的时候, 定义拥塞窗口大小为1;

每次收到一个ACK应答, 拥塞窗口加1;

每次发送数据包的时候, 将拥塞窗口和接收端主机反馈的窗口大小(对方主机的接收能力)做比较, 取较小的值作为实际发送的窗口(滑动窗口);

  • 滑动窗口=min(拥塞窗口,对方窗口大小)

像上面这样的拥塞窗口增长速度, 是指数级别的. "慢启动" 只是指初使时慢, 但是增长速度非常快.

如果前期发送报文都得到了确认应答,那应该网络也快恢复健康了,也应该加快发送的速度

为了不增长的那么快, 因此不能使拥塞窗口单纯的加倍.(拥塞窗口太大也没用,因为滑动窗口也受对方接收缓冲区大小的限制)
此处引入一个叫做慢启动的阈值

  • 当拥塞窗口超过这个阈值的时候, 不再按照指数方式增长, 而是按照线性方式增长
  • 当TCP开始启动的时候, 慢启动阈值等于窗口最大值;
  • 在每次超时重发的时候, 慢启动阈值会变成原来的一半, 同时拥塞窗口置回1;

慢启动的阈值:最近一次发生网络时拥塞窗口减半 

少量的丢包, 我们仅仅是触发超时重传; 大量的丢包, 我们就认为网络拥塞;
当TCP通信开始后, 网络吞吐量会逐渐上升; 随着网络发生拥堵, 吞吐量会立刻下降;
拥塞控制, 归根结底是TCP协议想尽可能快的把数据传输给对方, 但是又要避免给网络造成太大压力的折中方案.
上述拥塞控制机制是网络出现问题后的解决方法,一般无情况时滑动窗口的大小还是看对方缓冲区大小的接受能力。

面向字节流

由于双方都有发送和接受缓冲区, TCP程序的读和写不需要一一匹配(这就是面向字节流), 例如:

  • 写100个字节数据时, 可以调用一次write写100个字节, 也可以调用100次write, 每次写一个字节;
  • 读100个字节数据时, 也完全不需要考虑写的时候是怎么写的, 既可以一次read 100个字节, 也可以一次read一个字节, 重复100次;

UDP面向数据报:读和写要一一匹配,UDP发几次,接收方收几次(UDP没有发送缓冲区)

 TCP不关心上层协议和报文,只有字节的概念。接收方并不知道是一个报文还是半个,要应用层读上来判断和分析报文,用户对报文要一个一个处理:将字节流变成一个个完整的请求。那么要如何把字节分离成一个完整的请求呢?

 粘包问题

粘包问题是相对于应用层而言的,传输层TCP只有字节的概念;一般为了效率问题,应用层都是从TCP把字节全部取上,然后做分析处理的。

那么要如何解决粘包问题呢?(应用层定协议)

  1. 定长报文(每次发来的报文长度相同,明确每个报文之间的边界)
  2. 使用特殊字符来作为每个报文的边界
  3. 使用自描述字段+定长报头
  4. 使用自描述字段+特殊字符

 TCP异常情况

进程终止: 进程终止会释放文件描述符, 仍然可以发送FIN. 和正常关闭没有什么区别.
机器重启/关机: 先杀掉所有进程,和进程终止的情况相同
机器掉电/网线断开:客户端没有办法和服务器进行四次挥手,服务器不清楚客户端情况,维护正常情况,当客户端向重新向服务器发消息时,出现认知不一致,就会发送reset,要求服务器重新建立连接,服务器就把原链接关闭;如果客户端没有重发,服务器有保活机制。

  • 11
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值