TCP协议相关

#TCP特性

TCP是一个面向连接的、可靠的、基于字节流的传输层协议。

  • 面向连接。所谓的连接,指的是客户端和服务器的连接,在双方互相通信之前,TCP 需要三次握手建立连接,而 UDP 没有相应建立连接的过程。

  • 可靠性。TCP 使用了非常多机制的保证连接的可靠,这个可靠性体现在哪些方面呢?一个是有状态,另一个是可控制。

  • 面向字节流。UDP 的数据传输是基于数据报的,这是因为仅仅只是继承了 IP 层的特性,而 TCP 为了维护状态,将一个个 IP 包变成了字节流。

#TCP报文格式

 

  • 端口号:用来标识同一台计算机的不同的应用进程。

  • 源端口:源端口和IP地址的作用是标识报文的返回地址。

  • 目的端口:端口指明接收方计算机上的应用程序接口。(TCP报头中的源端口号和目的端口号同IP数据报中的源IP与目的IP唯一确定一条TCP连接。)

  • 序号和确认号:是TCP可靠传输的关键部分。序号是本报文段发送的数据组的第一个字节的序号。在TCP传送的流中,每一个字节一个序号。e.g.一个报文段的序号为300,此报文段数据部分共有100字节,则下一个报文段的序号为400。所以序号确保了TCP传输的有序性。确认号,即ACK,指明下一个期待收到的字节序号,表明该序号之前的所有数据已经正确无误的收到。确认号只有当ACK标志为1时才有效。比如建立连接时,SYN报文的ACK标志位为0。

  • 数据偏移/首部长度:4bits。由于首部可能含有可选项内容,因此TCP报头的长度是不确定的,报头不包含任何可选字段则长度为20字节,4位首部长度字段所能表示的最大值为1111,转化为10进制为15,15*32/8 = 60,故报头最大长度为60字节。首部长度也叫数据偏移,是因为首部长度实际上指示了数据区在报文段中的起始偏移值。

  • 保留:为将来定义新的用途保留,现在一般置0。

  • 控制位:URG ACK PSH RST SYN FIN,共6个,每一个标志位表示一个控制功能。

    1. URG:紧急指针标志,为1时表示紧急指针有效,为0则忽略紧急指针。

    2. ACK:确认序号标志,为1时表示确认号有效,为0表示报文中不含确认信息,忽略确认号字段。同时不要将确认序号ack与标志位ACK搞混,ack=发起方seq+1.

    3. PSH:push标志,为1表示是带有push标志的数据,指示接收方在接收到该报文段以后,应尽快将这个报文段交给应用程序,而不是在缓冲区排队。

    4. RST:重置连接标志,用于重置由于主机崩溃或其他原因而出现错误的连接。或者用于拒绝非法的报文段和拒绝连接请求。

    5. SYN:同步序号,用于建立连接过程,在连接请求中,SYN=1和ACK=0表示该数据段没有使用捎带的确认域,而连接应答捎带一个确认,即SYN=1和ACK=1。

    6. FIN:finish标志,用于释放连接,为1时表示发送方已经没有数据发送了,即关闭本方数据流。

  • 窗口:滑动窗口大小,用来告知发送端接受端的缓存大小,以此控制发送端发送数据的速率,从而达到流量控制。窗口大小16bit,因此窗口大小最大为65535。

  • 校验和:奇偶校验,此校验和是对整个的 TCP 报文段,包括 TCP 头部和 TCP 数据,以 16 位字进行计算所得。由发送端计算和存储,并由接收端进行验证。

  • 紧急指针:只有当 URG 标志置 1 时紧急指针才有效。紧急指针是一个正的偏移量,和顺序号字段中的值相加表示紧急数据最后一个字节的序号。 TCP 的紧急方式是发送端向另一端发送紧急数据的一种方式。

  • 选项和填充:最常见的可选字段是最长报文大小,又称为MSS(Maximum Segment Size),每个连接方通常都在通信的第一个报文段(为建立连接而设置SYN标志为1的那个段)中指明这个选项,它表示本端所能接受的最大报文段的长度。选项长度不一定是32位的整数倍,所以要加填充位,即在这个字段中加入额外的零,以保证TCP头是32的整数倍。

  • 数据部分: TCP 报文段中的数据部分是可选的。在一个连接建立和一个连接终止时,双方交换的报文段仅有 TCP 首部。如果一方没有数据要发送,也使用没有任何数据的首部来确认收到的数据。在处理超时的许多情况中,也会发送不带任何数据的报文段。

#TCP三次握手

 

 (1)第一次握手

客户端将标志位SYN设为1,同时产生一个随机seq = x,并将该数据包发送给服务器,客户端进入SYN_SENT状态,等待服务器确认

(2)第二次握手

 服务器收到数据包后由标志位SYN=1知道客户端请求建立链接,服务器将标志位SYN和ACK都置为1,ack=x+1,随机产生一个seq = y,并将该数据包发送给客户端确认,服务器进入SYN_RCVD状态。

(3)第三次握手

客户端收到确认后,检查ack是否为x+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=y+1,并将该数据包发送给服务器,服务器检查ack是否为y+1,ACK是否为1,如果正确则成功建立连接,服务器和客户端进入ESTABLISHED状态,完成三次握手,可以开始传送数据。

为什么要进行三次握手?

保证通信是“全双工”的,即客户端和服务器都具备发送和接收能力,假如没有客户端的第二次确认,那么服务端无法确保客户端已经接收到自己发出去的SYN确认包。而且假如只有两次握手,那么会存在资源浪费的情况:

客户端发出SYN请求后,由于网络复杂情况,这个请求一直没有发送到服务器端,这时候客户端超时重发第二个请求,然后第二个请求服务端正常接收并建立连接,数据传输完毕后双方断开了连接,但这时候第一次发送的那个SYN包终于到达服务端了,由于是二次握手,所以服务端发送确认包,并建立连接,但是客户端实际上已经断开连接了,而服务端建立了一条“没有”客户端的连接,造成了资源浪费。

#重传机制

三次握手结束之后就可以开始收发数据了,那要怎么保证我们发送出去的消息确实被接收到了呢?以寄快递为例,虽然我们寄快递前确认收件人信息无误,但是快递寄出去的时候,如果我们没有收到反馈(比如收件人告知你快递我已经收到了或者是快递公司的收件通知)。那么我们是无法知道快递是否准确送达的。

在TCP中,是通过序列号与确认应答来保证的。正常的传输过程如下:

但是实际情况是非常复杂的,假如网络出现丢包的情况要怎么办呢?这就涉及到TCP的重传机制

  • 超时重传
  • 快速重传
  • SACK
  • D-SACK

这里我们介绍超时重传与快速重传 

超时重传

在发送数据时,设定一个定时器,当超过指定的时间后,没有收到对方的 ACK 确认应答报文,就会重发该数据。

数据包丢失或者确认应答丢失时会发生超时重传:

 

问题来了,这个“特定的时间间隔”应该设置成多少合适呢?这就引入了RTT(Round-Trip Time 往返时延)的概念:

 

RTT简单来说就是数据一次往返的时间,超时重传时间是以 RTO (Retransmission Timeout 超时重传时间)表示。这个重传时间的设置比较玄学,设置高了或者低了都会有问题:

其实不用看图片大家应该也能猜到,超时时间过大,那么就会导致效率的降低(大部分时间都用来等待)。超时时间过低的话那就有可能造成不必要的重传(重传刚发出去结果就收到了应答)。

所以RTO的计算方法比较复杂,因为网络状态是时时刻刻变化的,而且也会存在波动较大的情况,所以RTO的值也是动态变化的,这里面的算法就不赘述了~~~有兴趣的小伙伴可以网上查找相关资料。

快速重传

我们一直强调网络情况是非常复杂的,所以上面提到的超时重传无法解决所有问题,设想这么一种场景:数据包只是因为某一个网络节点的异常而丢失了,实际上网路是“畅通”的,假如还是使用超时重传那么效率显然太低了,因此,快速重传机制诞生了。

  • 发送端发送了5份数据;
  • 接收方接收到seq1时返回ACK2,但是seq2由于网络异常丢包没有收到;
  • 这时候接收端又接收到seq3,在应答的时候依然返回ACK2;
  • 后续seq4和seq5也收到了,但是seq2还没有收到,依然返回ACK2。
  • 发送端连续收到三次同样的ACK2,知道seq2没有被接收到,就会在定时器任务触发之前重传seq2。
  • 接收端接收到seq2,而且检测到seq3、4、5都已经收到。所以返回ACK6

所以,快速重传的工作方式是当收到三个相同的 ACK 报文时,会在定时器过期之前,重传丢失的报文段。

但是快速重传也有问题,发送端只知道seq2丢失了,但是不知道seq3、4、5有没有被接收到。所以发送端并不清楚要不要把后续的seq3、4、5一并重传。

#滑动窗口

细心的小伙伴可能发现了,上面的图片里面并不是等到上一次数据的确认收到之后才发送下一批数据,因为串行发送数据效率太低,所以TCP引入了滑动窗口的概念。

有了窗口,就可以指定窗口大小,窗口大小就是指无需等待确认应答,而可以继续发送数据的最大值

窗口的实现实际上是操作系统开辟的一个缓存空间,发送方主机在等到确认应答返回之前,必须在缓冲区中保留已发送的数据。如果按期收到确认应答,此时数据就可以从缓存区清除。

假设窗口大小为 3 个 TCP 段,那么发送方就可以「连续发送」 3 个 TCP 段,并且中途若有 ACK 丢失,可以通过「下一个确认应答进行确认」。如下图:

#流量控制

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

接收端将自己可以接收的缓冲区大小放入 TCP 首部中的 "窗口大小" 字段,通过ACK端通知 发送端;

窗口大小字段越大,说明网络的吞吐量越高;

接收端一旦发现自己的缓冲区快满了,就会将窗口大小设置成一个更小的值通知给发送端;

发送端接受到这个窗口之后,就会减慢自己的发送速度;

如果接收端缓冲区满了,就会将窗口置为0;这时发送方不再发送数据,但是需要定期发送 一个窗口探测数据段,使接收端把窗口大小告诉发送端。 

接收端如何把窗口大小告诉发送端呢?回忆我们的TCP首部中,有一个16位窗口字段,就是存放了窗口 大小信息; 那么问题来了,16位数字最大表示65535,那么TCP窗口最大就是65535字节么? 实际上,TCP首部40字节选项中还包含了一个窗口扩大因子M,实际窗口大小是 窗口字段的值左移 M 位;

#拥塞控制

虽然TCP有了滑动窗口,能够高效可靠的发送大量的数据。但是如果在刚开始阶段就发送大 量的数据,仍然可能引发问题。 因为网络上有很多的计算机,可能当前的网络状态就已经比较拥堵。在不清楚当前网络状态下,贸然发 送大量的数据,是很有可能引起雪上加霜的。 TCP引入 慢启动 机制,先发少量的数据,探探路,摸清当前的网络拥堵状态,再决定按照多大的速度传输数据;

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

像上面这样的拥塞窗口增长速度,是指数级别的。"慢启动" 只是指初使时慢,但是增长速度非常快。 为了不增长的那么快,因此不能使拥塞窗口单纯的加倍。 此处引入一个叫做慢启动的阈值 当拥塞窗口超过这个阈值的时候,不再按照指数方式增长,而是按照线性方式增长。如下图:

#延迟应答

 如果接收数据的主机立刻返回ACK应答,这时候返回的窗口可能比较小。

假设接收端缓冲区为1M。一次收到了500K的数据;如果立刻应答,返回的窗口就是500K;

但实际上可能处理端处理的速度很快,10ms之内就把500K数据从缓冲区消费掉了;

  在这种情况下,接收端处理还远没有达到自己的极限,即使窗口再放大一些,也能处理过 来; 如果接收端稍微等一会再应答,比如等待200ms再应答,那么这个时候返回的窗口大小就是 1M; 一定要记得,窗口越大,网络吞吐量就越大,传输效率就越高。我们的目标是在保证网络不拥塞的情况 下尽量提高传输效率; 那么所有的包都可以延迟应答么?肯定也不是; 数量限制:每隔N个包就应答一次; 时间限制:超过最大延迟时间就应答一次; 具体的数量和超时时间,依操作系统不同也有差异;一般N取2,超时时间取200ms;

#捎带应答

在延迟应答的基础上,我们发现,很多情况下,客户端服务器在应用层也是 "一发一收" 的。意味着客 户端给服务器说了 "How are you",服务器也会给客户端回一个 "Fine, thank you"; 那么这个时候ACK就可以搭顺风车,和服务器回应的"Fine,thank you" 一起回给客户,就好比三次握手中的第二次握手同时发送ACK与SYN。,我们在延迟应答的机制上等待一段时间后将ACK与数据包一同发送提高效率。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值