TCP协议简单理解

tcp全称是“传输控制协议”。人如其名,要对数据的传输进行一个详细的控制。
tcp核心设计:
1.保证可靠性传输 能够及时的发现自己发送数据是成功还是失败
2.尽可能的提高传输效率
1,2进行权衡

TCP协议段格式

在这里插入图片描述从左到右,唐僧问路,16位源端口,16位目的端口,32位序号,32位确认序号,4位首部长度,16位窗口大小,16位检验和,16位紧急指针,选项,数据。

4位TCP报头长度:表示该TCP头部有多少个32位bit(有多少个4个字节);所有TCP头部最大长度是15*4=60字节

6位标志位

  • UGR:紧急指针是否有效
  • ACK:确认号是否有效,确认报文段
  • PSH:提示接收端应用程序立刻从TCP缓冲区把数据读走
  • RST:对方要求重新建立链接,我们把携带RST标识的称为复位报文段
  • SYN:请求建立连接,我们把携带SYN标识的称为同步报文段
  • FIN:通知对方,本端要关闭了,我们称携带FIN标识的为结束报文端

16位校验和:发送端填充,CRC校验。接受端校验不通过,则认为数据有问题。此处的校验和不光包含TCP首部,也包含TCP数据部分。
16位紧急指针:标识那部分数据是紧急数据

确认应答机制

TCP将每个字节的数据都进行了编号,即为序列号。
每个ACK都带有对应的确认序列号,(32位确认序号)意思是告诉发送者,我已经收到了哪些数据;下一次你从哪里开始发.

超时重传机制

  1. 超时时间依次变长(悲观的态度去面对丢包)
  2. 自动去重(通过序号进行去重),放到接收缓冲区里,接收包会有缓冲区的数据进行对比(序号),如有有的话,就会丢掉。
  3. 累计到一定的重传次数,TCP认为网络或者对端主机出现异常,强制关闭连接。

连接管理机制

  1. 建立连接(三次握手) syn(同步报文段,那个比特位设置位为1)与ack(确认报文段,那个比特位为1)都是内核控制发送的,第二次响应一起发送(同一时刻)。
    如果客户端第一次syn丢包,超时重传,如果客户端第一次ack 失败,超时重传。
    检查网络的联通状态,协商一些关键性数据。
    假设,第三次ack发送丢包,服务器没有收到ack,客户端不知道ack是否成功时,当客户端发送传输数据时,就会发现服务器没有进入ESTABLISHED状态,就会重新进行三次握手,建立连接。

  2. 断开连接(四次挥手)客户端发送FIN(结束报文段),服务器发送ack,发送FIN(两次不一定同时发送)。

服务端状态变化:

  • [CLOSED->LISTEN] 服务器端调用listen后进入LISTEN状态,等待客户端连接
  • [LISTEN->SYN_RCVD]一旦监听到连接请求(同步报文段),就将该连接放入内核当待队列中,并向客户端发送SYN确认报文。
  • [SYN_RCVD->ESTABLISHED]服务器一旦收到客户端的确认报文,就进入ESTABLEISHED状态,可以进行读写数据了
  • [ESTABLISHED->CLOSE_WAIT]当客户端主动关闭连接(调用close),服务器会收到结束报文段,服务器返回确认报文段并进入CLOSE_WAIT
  • [CLOSE_WAIT->LAST_ACK]进入CLOS_WAIT后说明服务器准备关闭连接(需要处理完之前的数据);当服务器真正调用close关闭连接时,会向客户端发送FIN,此时服务器进入LAST_ACK状态,等待最后一个ACK到来(这个ACK是客户端确认收到了FIN)
  • [LAST_ACK->CLOSED]服务器收到了对FIN的ACK,彻底关闭了连接

客户端状态转化

  • [CLOSED->SYN_SENT]客户端调用connect,发送同步报文段;
  • [SYN_SENT->ESTABLISHED]connect调用成功,则进入ESTABLISHED状态,开始读数据
  • [ESTABLISHED->FIN_WAIT_1]客户端主动调用close时,向服务器发送结束报文段,同时进入FIN_WAIT_1;
  • [FIN_WAIT_1->FIN_WAIT_2]客户端收到服务器对结束报文段的确认,则进入FIN_WAIT_2,开始等待服务器的结束报文段
  • [FIN_WAIT_2->TIME_WAIT]客户端收到服务器发来的结束报文段,进入TIME_WAIT,并发出LAST_ACK;
  • [TIME_WAIT->CLOSED]客户端要等待一个2MSL(MAX SEGMENT LIFE,报文最大生存时间)的时间,才会进入CLOSED状态

理解TIME_WAIT状态

如果我们先启动server,然后启动client,然后Ctrl-C使server终止,这是马上再运行server,结果是

$./server
bind error:Address already in use

虽然server的应用程序终止了,但是TCP协议层的连接并没有完全断开,因此不能再次监听同样的server端,我们用natsat命令查看一下:
发现服务器所处于的状态。

  • TCP协议规定,主动关闭连接的一方要处于TIME_WAIT状态,等待两个MLS(报文最大生存时间)的时间才能回到CLOSED状态。
  • 我们使用Ctrl+c终止了server,所以server是主动关闭连接的一方,在TIME_WAIT期间仍然不能再次监听同样的server端口
  • MSL在RFC1122中规定为2分钟,但是各操作系统的实现不同,在Centos7默认配置的值是60s
  • 可以通过cat/proc/sys/net/ipv4/tcp_fin_timeout 查看msl的值

为什么TIME_WAIT的时间是2MSL?

  • MSL是TCP报文的最大生存时间,因此TIME_WAIT持续存在2MSL的话,就能保证在两个传输方式上的尚未被接收或迟到的报文段都已经消失(否则服务器立刻重启,可能会收到来自上一个进程的迟到的数据,但是这种数据很可能是错误的);同时也是理论上保证最好一个报文可靠到达(假设最后一个ACK丢失,那么服务器再重发一个FIN。这时虽然客户端的进程不在了,但是TCP连接还在,仍然可以重发LAST_ACK).

对于服务器上出现大量的CLOSE_WAIT状态,原因就是服务器没有正确关闭socket,导致四次挥手没有正确完成。这是一个bug,只需要加上对应的close即可解决问题。

滑动窗口

提高传输效率,批量发送,批量等待ACK
对每一个发送的数据段,都要给一个ACK确认应答。收到ACK后再发送下一个数据段。这样做有一个比较大的问题。就是性能比较差,尤其是数据往返的时间比较长的时候。
既然这样一发一收的方式性能比较低,那么我们一次发送多条数据,就可以大大的提高性能(其实是将多个的等待时间重叠在一起了)。
窗口大小指的是无需等待确认应答而可以继续发送数据的最大值
收到第一个ACK后,滑动窗口向后移动,继续发送下一段的数据,以此类推。
操作系统内核为了维护这个滑动窗口,需要开辟发送缓冲区来记录当前还有哪些数据没有应答,只要确认过应答的数据,才能从缓冲区删掉。(防止出现丢包)
窗口越大,则网络的吞吐率就越高

  1. 情况一:数据包已经抵达,ACK被丢掉。
    这种情况下,部分ACK丢了并不要紧,因为可以通过后续的ACK进行确认。后续的ACK确认信息会覆盖前面ACK的信息。
  2. 情况二:数据包直接丢了
    当某一段报文段丢失之后,发送端会一直收到丢失数据包的序列号,就像是提醒发送端“我要某个序列号”。如果发送端主机连续三次收到了同样一个"ACK"确认信号的应答,就会将丢失的数据包重新进行发送。这个时候接收端收到了丢失的数据包之后,根据接收端操作系统内核的接收缓冲区中的信息,这种机制被称为“高速重发控制”(也叫“快重传”)
    快速重传和超时重传的本质是一样的,借助了确认序号的特点,能够做到只重传丢了哪一个数据,其他正确到达的数据无需重复传输。

窗口越大,传输效率越高
窗口不能无限大,传输效率建在可靠性上

  1. 发送方如果发送太快,接收方可能除了不过来
    发送方发送的数据先要到达接收方的网卡,然后放到内核的接收缓冲区中。接收方的用户代码从接收缓冲区取数据(recv/read)
    限制方法:流量控制->控制窗口大小
  2. 如果当前网络比较拥堵,发送方如果发送的很快,仍然会丢包
    限制方式:拥塞控制->控制窗口大小
    真实的发送方的窗口大小是流量控制和拥塞控制两者的最小值。

流量控制

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

  • 接收端将自己可以接收的缓冲区大小放入TCP首部中的“窗口大小”字段,通过ACK端通知发送端;窗口大小字段越大,说明网络的吞吐量越高。
  • 接收端一旦发现自己的缓冲区快满了,就会将窗口大小设置成一个更小的值通知给发送端
  • 发送端接收到这个窗口之后,就会减慢自己的发送速度
  • 如果接受端缓冲区满了,就会将窗口置为0,这时发送方不再发送数据,但是需要定期发送一个窗口探测数据端,使接受端把窗口大小告诉给发送端。

拥塞控制

虽然TCP有了滑动窗口这个大杀器,能够高效可靠的发送大量数据,但是如果在刚开始阶段就发送大量的数据,仍然可能引发问题。因为网络上有很多的计算机,可能当前的网络状态就已经比较拥堵。在不清楚当前网络状态下,贸然发送大量的数据,是很有可能引起雪上加霜的。

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

  • 为此引入一个概念称为拥塞窗口
  • 发送开始的时候,定义拥塞窗口大小为1
  • 每次收到一个ACK应答,拥塞窗口加1
  • 每次发送数据包的时候,将拥塞窗口和接受端主机反馈的窗口大小做比较,取较小的值作为实际发送的窗口。

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

  • 为了不增长的那么快,因为不能使用拥塞窗口的单纯的加倍。
  • 为此引入了一个叫做慢启动的阈值
  • 当拥塞窗口超过这个阈值的时候,不再按照指数方式增长,而是按照线性方式增长
  • 当TCP开始启动的时候,慢启动阈值等于窗口最大值
  • 在每次超时重发的时候,慢启动阈值会变成原来的一半,同时拥塞窗口置为1

少量的丢包,我们仅仅是触发超时重传;大量的丢包,我们认为网络拥塞
当TCP通信开始后,网络吞吐量会逐渐上升,随着网络发送拥堵,吞吐量会立刻下降
拥塞控制,归根到底是TCP协议想尽可能快的把数据传输给对方,到那时又要避免给网络造成太大压力的折中方案

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章