我们已经知道16位窗口大小就是对方的缓冲区剩余空间大小
缓冲区的接受能力是有上限的,超过缓冲区容量就会造成浪费,低效。所以需要有流量控制,以合理的流量发送数据。
流量控制是变得合理,更侧重于效率
发送端如何尽早得知对方的接受能力?按量按需发送必须知道对方的接受缓冲区中剩余空间的大小。我们所构建的报文都是对方给的,就可以知道对方缓冲区的剩余空间大小
标志位

我们发送的数据至少要有完整的tcp报头,而报头是有不同的类型的,我们通过标志位来标识不同的报文类型。针对不同的报文类型,接收方要有不同的做法。我们交换的数据都是tcp报文段,带上tcp协议当中的标志位来区分报文类型
标志位的本质就是报头中的比特位
ACK :表明报文是一个应答报文,ACK标志位几乎是常设为1,(1有效,0无效)
PSH:系统调用条件就绪,把数据发给对方,放在对方的接收缓冲区里,通知对方上层应用程序尽快处理
RST:重新建立连接,出现任何问题都可以重新建立连接
我们建立三次握手是不一定会成功的
当ACK应答丢失,且超时重传后仍然不成功

FIN :通知对方,本端要关闭了,携带FIN标识的就是结束报文段
SYN :请求建立连接,携带SYN标识的就是同步报文段
URG:紧急指针是否有效,配合16位紧急指针(标识紧急数据)
tcp保证可靠性-->序号 --> 按序到达
---> 接受缓冲区,字节流时的接受队列
如果我们有数据想被优先处理,就可以设置为紧急数据,但优先读取不是主流。紧急数据不是常规数据,是带外数据,只有一个字节
三次握手
我们通信之前要有三次握手来建立通信连接,这样以后才可以发送数据
如下图:

可靠性的本质:我收到了应答,对方就收到了
四次挥手
断开连接也要征求双方同意,本质上是关闭全双工

超时重传
没有收到ACK无法100%保证对方是否收到消息,无法保证可靠性
我们也无法确定是数据丢了导致对方没有收到还是因为对方收到了但是应答丢了。所以我们只能等待。而我们等待的本质就是在等应答
如果是应答丢了,已经收到了数据,但是在特定的时间间隔内没有收到应答我们就重发数据,就会导致报文重复。但是根据序号我们可以很好地做到去重,丢弃重复的包
所以,特定时间间隔,收不到应答,我们就判定报文丢失
丢包:收不到应答+超时(超过时间间隔)
数据丢: 应答丢:


特定的时间间隔?超时时间?是多久
是动态变化的,网络环境是不同的
连接管理机制
不同的连接有不同的状态
connect只是发起三次握手握手,三次握手后续过程由客户端os和服务端os自动完成
accept是把建立好的连接获取上来,不参与三次握手
为什么要进行三次握手?
1.以最小的成本,100%确认双方的通信意愿
2.以最短的方式,验证全双工。本质是验证我们两个所处的网络是通畅的,能够支持全双工
三次握手本质是四次握手
我们也可以举个例子
A:我们加个好友一起玩吧
B:好啊
B:什么时候呀
A:现在
C-->S:我要连接你,我的序号是Y
S-->C:我收到你的序号Y
S-->C:我同意连接你,我们序号是X
C-->S:我收到你的序号X

++因为服务端接收客户端的请求时是无脑接受的,所以我们把ACK+SYN做捎带应答,压缩成一个报文。即原来发两个报文。ACK先置1,再SYN置1,现在压缩成一个报文,ACK,SYN都置为1.也就是三次握手,最小的成本++
本质是四次,三次还是四次是由场景决定的
time_wait和close_wait
主动申请关闭连接的一方会进入time_wait状态,2个MSL时间后才会进入closed状态
2个MSL?
主动关闭连接的一方只是不再发送数据,但是可以接收数据,被动关闭的一方在申请关闭连接之前同样可以发送数据,主动关闭的一方可以继续接收剩余数据。
如果没有time_wait状态而立即是closed状态。就会有残留数据又出现,但是连接关闭,就会影响下次通信过程,,导致通信错误。
所以需要2个MSL时间,MSL是tcp报文的最大生存时间,2msl就能保证两个传输方向上的尚未被接收或迟到的报文都已经消失
被动关闭的一方发出FIN后,没有收到ACK,就会二次SIN补发
所以是主动断开连接的一方发送FIN接收ACK,此时主动的一方不发送数据但是可以接收数据也可以处理数据。当被动的一方也发送FIN并收到我们的ACK之后主动的一方进入time_wait状态,被动的一方在收到ACK之后就进入closed状态了。双方都不发数据也不接收数据,只是等待2MSL时间来等待剩余报文消失过期,2MSL时间后主动的一方进入closed状态,彻底终止连接。可是为什么要有time_wait? time_wait状态下不接受也不处理旧连接数据,选择直接丢弃旧数据,是为后续的连接清扫障碍。因为新连接的四元组标识和旧连接的是一样的,避免新连接被旧数据干扰。因为time_wait状态是在被动断开连接收到ACK之后,如果没有收到ACK还是可以继续重发让主动的一方接收数据。如果没有time_wait状态,旧数据没有被丢弃处理,而直接closed状态,而我们新连接的四元组标识和旧连接的是一样的,那旧连接的数据就会影响到新的连接,导致通信错误。所以需要我们时长为2MSL的time_wait状态

断开后是无法立即重启的,bind_eror。因为自己的连接并未释放,端口号还是在time_wait状态下被占用的,无法二次启动绑定源端口号,要time_wait释放连接后双方都closed的状态下
setsockopt()可以实现立即重启,本质上是端口复用,取消time_wait
滑动窗口
滑动窗口的大小由对方的接收能力决定(就是报文中16位窗口大小)
发送方,一次向对方发送多少数据由滑动窗口大小决定,滑动窗口大小是指无需等待确认应答而可以继续发送数据的最大值。我们的发送缓冲区就是用来维护滑动窗口的核心载体

数据已确认已应答后就是无效的了,我们不需要刻意地区清空缓冲区,数据原来占有的空间可以直接再次使用
start的位置由最新的确认序号决定,序号增加,确认序号增加,start就会向右移
滑动窗口的本质:流量控制的具体实现方案,是流量控制实现的底层技术
出现丢包如何进行重传?
- 数据到达,但ACK应答丢失,滑动窗口正常工作,照样向右更新

应答丢失可以通过后续的ACK进行确认,因为确认序号之前的报文已经全部收到
- 数据包丢

丢包了滑动窗口不会跳过报文进行应答,不跳跃地连续发送确认序号
滑动窗口左侧是不会向右滑动的,是为了进行超时重传和快重传
tcp发出的报文数据暂时没有应答的时候,必须让对应的报文保存到滑动窗口,所以重传的都是滑动窗口里的数据,重传后才会向右滑动,因为确认序号变了,start的位置就变了
- 滑动窗口一直向右是不会溢出的
我们可以看成一个环
- 快重传?超时重传?
每一个报文默认打开超时重传和快重传,一段时间内收到了3个一样的ack立马启动快重传,同时取消超时重传。其他的情况就是超时重传(兜底)
5.最左侧的数据丢了滑动窗口不变
中间的报文丢失?最右侧的丢失呢?
都可以归结为最左侧报文丢失的情况,因为滑动窗口会滑动到数据丢失的位置,重传后才会继续向右滑动
-
滑动窗口不会向左滑动,因为下标是一直增大的
-
发送数据其实是把数据拷贝到发送话冲去的待发送的后面 (uu)
流量控制
三次握手的时候就交换了双方的缓冲区的数据
缓冲区满的时候再发送数据就会导致丢包,引起丢包重传等
tcp支持根据接收端的处理能力来决定发送端的发送速度,这个机制就叫做流量控制
拥塞控制
慢启动机制