TCP详解

129

文章内容参考

TCP 协议如何保证可靠传输
TCP协议详解
tcp的半连接与完全连接队列
三次握手四次挥手满分回答

TCP 最主要的特点

  • TCP 是面向连接的传输层协议。应用程序在使用 TCP 协议之前,必须先建立 TCP 连接。在传送数据完毕后,必须释放已经建立的 TCP 连接
  • 每一条 TCP 连接只能有两个端点,每一条 TCP 连接只能是点对点的(一对一)
  • TCP 提供可靠交付的服务。通过 TCP 连接传送的数据,无差错、不丢失、不重复,并且按序到达
  • TCP 提供全双工通信。TCP 允许通信双方的应用进程在任何时候都能发送数据。TCP 连接的两端都设有发送缓存和接受缓存,用来临时存放双向通信的数据
  • 面向字节流。TCP 中的“流”指的是流入到进程或从进程流出的字节序列

TCP 虽然是面向字节流的,但 TCP 传送的数据单元却是报文段。一个 TCP 报文段分为首部和数据两部分。TCP 报文段首部的前20个字节是固定的,后面有4n字节是根据需要而增加的选项(n是整数)。因此 TCP 首部的最小长度是20字节

TCP报文首部格式

在这里插入图片描述
首部字段

  • 源端口和目的端口 各占2个字节,分别写入源端口号和目的端口号
  • 序号 占4字节。序号范围是[0, 232-1],共232(即4 294 967 296)个序号。序号增加到232-1后,下一个序号就又回到0。在一个 TCP 连接中传送的字节流中的每一个字节都按顺序编号
  • 确认号 占4字节,是期望收到对方下一个报文段的第一个数据字节的序号
  • 数据偏移 占0.5个字节,它指出 TCP 报文段的数据起始处距离 TCP 报文段的起始处有多远。这个字段实际上是指出 TCP 报文段的首部长度
    保留 占6位,保留为今后使用,但目前应置为0

下面有6个控制位,用来说明本报文段的性质

  • 紧急 URG(URGent) 当 URG=1 时,表明紧急指针字段有效。它告诉系统此报文段中有紧急数据,应尽快传送(相当于高优先级的数据),而不是按原先的排队顺序来传送
  • 确认 ACK(ACKnowledgment) 仅当 ACK=1 时确认号字段才有效。当 ACK=0 时,确认号无效。TCP 规定,在连接建立后所有传送的报文段都必须把 ACK 置1
  • 推送 PSH(Push) 当两个应用进程进行交互式的通信时,有时在一端的应用进程希望在键入一个命令后立即就能够收到对方的响应
  • 复位 RST(ReSeT) 当 RST=1 时,表明 TCP 连接中出现严重差错(如由于主机崩溃或其他原因),必须释放连接,然后再重新建立运输连接
  • 同步 SYN(SYNnchronization) 在连接建立时用来同步序号。当 SYN=1 而 ACK=0 时,表明这是一个连接请求报文段。对方若同意建立连接,则应在响应的报文段中使 SYN=1 和 ACK=1
  • 终止 FIN(FINis) 用来释放一个连接。当 FIN=1 时,表明此报文段的发送发的数据已发送完毕,并要求释放运输连接
  • 窗口 占2字节。窗口值是[0, 216-1]之间的整数。窗口值作为接收方让发送方设置其发送窗口的依旧
  • 检验和 占2字节。检验和字段检验的范围包括首部和数据这两部分
  • 紧急指针 占2字节。紧急指针仅在 URG=1 时才有意义,它指出本报文段中的紧急数据的字节数

三次握手建立链接

在这里插入图片描述

  • SYN:连接请求/接收 报文段
  • seq:发送的第一个字节的序号
  • ACK:确认报文段
  • ack:确认号。希望收到的下一个数据的第一个字节的序号

刚开始客户端处于 Closed 的状态,而服务端处于 Listen 状态:

CLOSED :没有任何连接状态
LISTEN :侦听来自远方 TCP 端口的连接请求

1)第一次握手:客户端向服务端发送一个 SYN 报文(SYN = 1),并指明客户端的初始化序列号 ISN(x),即图中的 seq = x,表示本报文段所发送的数据的第一个字节的序号。此时客户端处于 SYN_Send 状态。

SYN-SENT :在发送连接请求后等待匹配的连接请求

2)第二次握手:服务器收到客户端的 SYN 报文之后,会发送 SYN 报文作为应答(SYN = 1),并且指定自己的初始化序列号 ISN(y),即图中的 seq = y。同时会把客户端的 ISN + 1 作为确认号 ack 的值,表示已经收到了客户端发来的的 SYN 报文,希望收到的下一个数据的第一个字节的序号是 x + 1,此时服务器处于 SYN_REVD 的状态。

SYN-RECEIVED:在收到和发送一个连接请求后等待对连接请求的确认

3)第三次握手:客户端收到服务器端响应的 SYN 报文之后,会发送一个 ACK 报文,也是一样把服务器的 ISN + 1 作为 ack 的值,表示已经收到了服务端发来的的 SYN 报文,希望收到的下一个数据的第一个字节的序号是 y + 1,并指明此时客户端的序列号 seq = x + 1(初始为 seq = x,所以第二个报文段要 +1),此时客户端处于 Establised 状态。

服务器收到 ACK 报文之后,也处于 Establised 状态,至此,双方建立起了 TCP 连接。

ESTABLISHED:代表一个打开的连接,数据可以传送给用户

初始化序列号(ISN)

ISN不是固定的
RFC1948中提出了一个较好的初始化序列号ISN随机生成算法。

ISN = M + F(localhost, localport, remotehost, remoteport)
其中M是一个计时器,每4毫秒加1。F是一个Hash算法,比如MD5或者SHA256。

为什么ISN不采用一个固定的值

1.稳定传输方面
防止同一个连接的不同实例(different instantiations/incarnations of the same connection)的数据包混淆。
(源IP、源端口号、目的地址、目的端口号)这个四元组唯一标识一个TCP连接,当一个TCP连接在经历四次挥手关闭时,假如有一个数据包延迟特别大,而这个连接在关闭后又马上以相同的四元组建立起来,那么先前这个连接的TCP数据包到达的时候,如果系列号还落在接收窗内,那么这个数据包就可能会被错误接收

2.安全方面
TCP系列号欺诈
在这里插入图片描述

C冒充B与A建立链接

第三次握手ACK应答服务器没有收到

原因:
1.服务器的SYN-ACK包未送达
2.客户端的ACK包未送达

如果服务器端接到了客户端发的SYN后回了SYN-ACK后客户端掉线了,服务器端没有收到客户端回来的ACK,那么,这个连接处于一个中间状态,既没成功,也没失败。于是,服务器端如果在一定时间内没有收到的TCP会重发SYN-ACK。在Linux下,默认重试次数为5次,重试的间隔时间从1s开始每次都翻倍,5次的重试时间间隔为1s, 2s, 4s, 8s, 16s,总共31s,第5次发出后还要等32s才知道第5次也超时了,所以,总共需要 1s + 2s + 4s+ 8s+ 16s + 32s = 63s,TCP才会断开这个连接。如果重传次数超过系统规定的最大重传次数,则系统将该连接信息从半连接队列中删除。

server端的半连接队列(syn队列)

在三次握手协议中,服务器维护一个半连接队列,该队列为每个客户端的SYN包开设一个条目(服务端在接收到SYN包的时候,就已经创建了request_sock结构,存储在半连接队列中),该条目表明服务器已收到SYN包,并向客户发出确认,正在等待客户的确认包(会进行第二次握手发送SYN+ACK 的包加以确认)。这些条目所标识的连接在服务器处于Syn_RECV状态,当服务器收到客户的确认包时,删除该条目,服务器进入ESTABLISHED状态。
该队列为SYN 队列,长度为 max(64, /proc/sys/net/ipv4/tcp_max_syn_backlog) ,在机器的tcp_max_syn_backlog值在/proc/sys/net/ipv4/tcp_max_syn_backlog下配置。

server端的完全连接队列(accpet队列)

当第三次握手时,当server接收到ACK 报之后, 会进入一个新的叫 accept 的队列,该队列的长度为 min(backlog, somaxconn),默认情况下,somaxconn 的值为 128,表示最多有 128 的 ESTAB 的连接等待 accept(),而 backlog 的值则应该是由 int listen(int sockfd, int backlog) 中的第二个参数指定,listen 里面的 backlog 可以有我们的应用程序去定义的。

SYN 洪泛攻击

SYN 攻击就是 Client 在短时间内伪造大量不存在的 IP 地址,并向 Server 不断地发送 SYN 包,Server 则回复确认包,并等待 Client 确认,由于源地址不存在,因此 Server 需要不断重发直至超时,这些伪造的 SYN 包将长时间占用半连接队列,导致正常的 SYN 请求因为队列满而被丢弃,从而引起网络拥塞甚至系统瘫痪。

可靠的传输

TCP 协议保证可靠传输的手段:

1)校验和 Checksum
2)序列号和确认应答机制
3)重传机制
4)流量控制(滑动窗口协议)
5)拥塞控制

校验和

所谓 TCP 的校验和(Checksum)就是说:由发送端计算待发送 TCP 报文段的校验和,然后接收端对接收到的 TCP 报文段验证其校验和(TCP 的校验和是一个端到端的校验和)。其目的是为了发现 TCP 的首部和数据在发送端到接收端之间是否发生了变动。如果接收方检测到校验和有差错,则该 TCP 报文段会被直接丢弃

序列号和确认应答机制

在这里插入图片描述

重传机制

  • 超时重传
  • 快速重传

① 超时重传

大概一说到重传大家第一个想到的就是超时重传吧。超时重传就是 TCP 发送方在发送报文的时候,设定一个定时器,如果在规定的时间内没有收到接收方发来的 ACK 确认报文,发送方就会重传这个已发送的报文段。

超时重传时间我们一般用 RTO(Retransmission Timeout) 来表示,那么,这个 RTO 设置为多少最合适呢,也就是说经过多长时间进行重传最好?
在这之前,我们先讲解一下 RTT(Round-Trip Time 往返时延) 的概念:RTT 就是数据从网络一端传送到另一端所需的时间,也就是报文段的往返时间。

超时重传时间 RTO 的值应该略大于报文往返 RTT 的值

② 快速重传

快速重传(Fast Retransmit)机制不以时间为驱动,而是以数据驱动重传
快速重传机制的原理:每当接收方收到比期望序号大的失序报文段到达时,就向发送方发送一个冗余 ACK,指明下一个期待字节的序号。
举个例子:发送方已经发送 1、2、3、4、5报文段

接收方收到报文段 1,返回 1 的 ACK 确认报文(确认号为报文段 2 的第一个字节)
接收方收到报文段 3,仍然返回 1 的 ACK 确认报文(确认号为报文段 2 的第一个字节)
接收方收到报文段 4,仍然返回 1 的 ACK 确认报文(确认号为报文段 2 的第一个字节)
接收方收到报文段 5,仍然返回 1 的 ACK 确认报文(确认号为报文段 2 的第一个字节)
接收方收到 3 个对于报文段 1 的冗余 ACK,认为报文段 2 丢失,于是重传报文段 2
最后,接收方收到了报文段 2,此时因为报文段 3、4、5 都收到了,所以返回 6 的 ACK 确认报文(确认号为报文段 6 的第一个字节)

在这里插入图片描述

什么是流量控制?流量控制的目的?

如果发送者发送数据过快,接收者来不及接收,那么就会有分组丢失。为了避免分组丢失,控制发送者的发送速度,使得接收者来得及接收,这就是流量控制。流量控制根本目的是防止分组丢失,它是构成TCP可靠性的一方面。

如何实现流量控制?

由滑动窗口协议(连续ARQ协议)实现。滑动窗口协议既保证了分组无差错、有序接收,也实现了流量控制。主要的方式就是接收方返回的 ACK 中会包含自己的接收窗口的大小,并且利用大小来控制发送方的数据发送。

流量控制引发的死锁?怎么避免死锁的发生?

当发送者收到了一个窗口为0的应答,发送者便停止发送,等待接收者的下一个应答。但是如果这个窗口不为0的应答在传输过程丢失,发送者一直等待下去,而接收者以为发送者已经收到该应答,等待接收新数据,这样双方就相互等待,从而产生死锁。
为了避免流量控制引发的死锁,TCP使用了持续计时器。每当发送者收到一个零窗口的应答后就启动该计时器。时间一到便主动发送报文询问接收者的窗口大小。若接收者仍然返回零窗口,则重置该计时器继续等待;若窗口不为0,则表示应答报文丢失了,此时重置发送窗口后开始发送,这样就避免了死锁的产生。

拥塞控制

所谓拥塞就是说:在某段时间,对网络中某一资源的需求超过了该资源所能提供的可用部分(即 需大于供),网络的性能变差。
区别:流量控制是为了让接收方能来得及接收,而拥塞控制是为了降低整个网络的拥塞程度,防止过多的数据注入到网络中。

采用算法

  • 慢开始
  • 拥塞避免
  • 快重传
  • 快恢复

慢开始

慢开始的思路就是:TCP 在刚建立连接完成后,如果立即把大量数据字节注入到网络,那么很有可能引起网络阻塞。好的方法是先探测一下,一点一点的提高发送数据包的数量,即由小到大逐渐增大拥塞窗口数值。cwnd 初始值为 1,每经过一个传播轮次,cwnd 加倍(指数增长)。

在这里插入图片描述

当然不能一直执行慢启动,这里会设置一个慢启动轮限 ssthresh 状态变量:

当 cwnd < ssthresh 时,继续使用慢启动算法
当 cwnd >= ssthresh 时,开始使用「拥塞避免算法」

拥塞避免

拥塞避免算法的思路是让拥塞窗口 cwnd 缓慢增大,即每经过一个往返时间 cwnd 加 1。
在这里插入图片描述

无论是慢开始阶段还是拥塞避免,只要出现了网络拥塞(触发超时重传机制),慢开始轮限 sshresh 和 拥塞窗口大小 cwnd 的值会发生变化(乘法减小):

ssthresh 设为 cwnd/2
cwnd 重置为 1
由于拥塞窗口大小重置为 1 了,所以就会重新开始执行慢启动算法。

在这里插入图片描述

快重传

要求接收方每收到一个失序的报文段后就立即发出重复确认而不是等待自己发送数据时才捎带确认

发送方只要一连收到三个重复确认就立即重传对方尚未收到的报文段,而不必等待设置的重传计如果在拥塞阶段发生了快速重传就没有必要像超时重传那样处理拥塞窗口了,因为此时的拥塞并不是很严重。
上面已讲

快恢复

当发送方连续收到三个重复确认时,就执行“乘法减小”算法,把慢开始门限ssthresh减半,为了预防网络拥塞

将拥塞窗口cwnd值设置为慢开始门限ssthresh减半后的数值,然后开始执行拥塞避免算法

在这里插入图片描述

在这里插入图片描述
TCP Tahoe版本与TCP Reno版本的区别:Reno版本在快重传之后采用快恢复算法而不是采用慢开始算法

注意:
接收方根据自己的接收能力设定了接收窗口rwnd,将此窗口值写入TCP首部传送给对方。结合拥塞控制,发送方的发送窗口一定不能大于接收方给出的接收窗口且一定不能大于自己的拥塞窗口

发送方的发送窗口的上限值 = Min[rwnd, cwnd]

选择性确认(sack)

在早期的TCP拥塞控制,快重传机制中,发送方通过收到三个重复ack来告诉发送者某个丢包了,然后进入快速重传状态,发送方重传这个包,当接收方收到这个重传包后,便会发一个ack(ack新的包)给发送者某,告诉发送者某下一个要发送的包是哪一个。可以看到,通过重复确认每次只能重传一个包,如果有多个丢包,在等待重传过程中,很容易timeout,造成带宽利用率下降(underutilized)。
  而如果开启sack,每一个sack段记录的是已经收到的连续的包,sack段与sack段之间断片的,也就是还没收到的(可能已经丢失,也可能是reorder)。通过sack段便可以知道多个可能已经丢失的包,这样便可以一次性的重传,而不是一个一个重传,避免因等待时间长造成的timeout问题。
  要注意的是开启sack选项,也是有弊端的,因为丢包意味着网络很可能已经拥塞,这时如果一次重传多个包,很可能会造成网络更加拥塞。

四次挥手断开连接

建立一个 TCP 连接需要三次握手,而终止一个 TCP 连接要经过四次挥手(也有将四次挥手叫做四次握手的)。这是由于 TCP 的半关闭(half-close)特性造成的,TCP 提供了连接的一端在结束它的发送后还能接收来自另一端数据的能力。

TCP 连接的释放需要发送四个包(执行四个步骤),因此称为四次挥手(Four-way handshake),客户端或服务端均可主动发起挥手动作。

在这里插入图片描述

  • FIN :连接终止位
  • seq:发送的第一个字节的序号
  • ACK:确认报文段
  • ack:确认号。希望收到的下一个数据的第一个字节的序号

刚开始双方都处于ESTABLISHED 状态,假设是客户端先发起关闭请求。四次挥手的过程如下:

1)第一次挥手:客户端发送一个 FIN 报文(请求连接终止:FIN = 1),报文中会指定一个序列号 seq = u。并停止再发送数据,主动关闭 TCP 连接。此时客户端处于 FIN_WAIT1 状态,等待服务端的确认。

FIN-WAIT-1 - 等待远程TCP的连接中断请求,或先前的连接中断请求的确认;

2)第二次挥手:服务端收到 FIN 之后,会发送 ACK 报文,且把客户端的序号值 +1 作为 ACK 报文的序列号值,表明已经收到客户端的报文了,此时服务端处于 CLOSE_WAIT 状态。

CLOSE-WAIT - 等待从本地用户发来的连接中断请求;

此时的 TCP 处于半关闭状态,客户端到服务端的连接释放。客户端收到服务端的确认后,进入FIN_WAIT2(终止等待 2)状态,等待服务端发出的连接释放报文段。

FIN-WAIT-2 - 从远程TCP等待连接中断请求;

此时服务器还可以发送文发送的数据
客户端还可以接受为接收的状态

3)第三次挥手:如果服务端也想断开连接了(没有要向客户端发出的数据),和客户端的第一次挥手一样,发送 FIN 报文,且指定一个序列号。此时服务端处于 LAST_ACK 的状态,等待客户端的确认。

LAST-ACK - 等待原来发向远程TCP的连接中断请求的确认;

4)第四次挥手:客户端收到 FIN 之后,一样发送一个 ACK 报文作为应答(ack = w+1),且把服务端的序列值 +1 作为自己 ACK 报文的序号值(seq=u+1),此时客户端处于 TIME_WAIT (时间等待)状态。

TIME-WAIT - 等待足够的时间以确保远程TCP接收到连接中断请求的确认;

这个时候由服务端到客户端的 TCP 连接并未释放掉,需要经过时间等待计时器设置的时间 2MSL(一个报文的来回时间) 后才会进入 CLOSED 状态(这样做的目的是确保服务端收到自己的 ACK 报文。如果服务端在规定时间内没有收到客户端发来的 ACK 报文的话,服务端会重新发送 FIN 报文给客户端,客户端再次收到 FIN 报文之后,就知道之前的 ACK 报文丢失了,然后再次发送 ACK 报文给服务端)。服务端收到 ACK 报文之后,就关闭连接了,处于 CLOSED 状态。

② 为什么要四次挥手
由于 TCP 的半关闭(half-close)特性,TCP 提供了连接的一端在结束它的发送后还能接收来自另一端数据的能力。

任何一方都可以在数据传送结束后发出连接释放的通知,待对方确认后进入半关闭状态。当另一方也没有数据再发送的时候,则发出连接释放通知,对方确认后就完全关闭了TCP连接。

通俗的来说,两次握手就可以释放一端到另一端的 TCP 连接,完全释放连接一共需要四次握手。

举个例子:A 和 B 打电话,通话即将结束后,A 说 “我没啥要说的了”,B 回答 “我知道了”,于是 A 向 B 的连接释放了。但是 B 可能还会有要说的话,于是 B 可能又巴拉巴拉说了一通,最后 B 说“我说完了”,A 回答“知道了”,于是 B 向 A 的连接释放了,这样整个通话就结束了。

  • 6
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

binary~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值