TCP浅析

TCP 简介

第一部分先为大家介绍一下 TCP 的主要概念,并讲解一下 TCP 的三个重要特性——1. 面向连接;2. 基于字节流;3. 可靠性。

下图就是一种经典的OSI分层模型,可以看到 TCP 在网络分层中的位置。
在这里插入图片描述

网络分层模型

本文重点对 TCP 进行介绍,从图中可以看到 TCP 位于传输层,而且构建于网络层的 IP 协议之上,TCP 是一种面向连接的、可靠的、基于字节流的传输层通信协议。

seq与ack

seq和ack号存在于TCP报文段的首部中,seq是序号,ack是确认号,大小均为4字节。

seq:占 4 字节,序号范围[0,2^32-1],序号增加到 2^32-1 后,下个序号又回到 0。TCP 是面向字节流的,通过 TCP 传送的字节流中的每个字节都按顺序编号,而报头中的序号字段值则指的是本报文段数据的第一个字节的序号。
ack:占 4 字节,期望收到对方下个报文段的第一个数据字节的序号。

TCP报文格式

在这里插入图片描述

TCP报文首部最短为20字节,每一行的长度均为4个字节。

注意:

  • TCP的包是没有IP地址的,那是IP层上的事。但是有源端口和目标端口。
  • 一个TCP连接需要四个元组来表示是同一个连接(src_ip, src_port, dst_ip, dst_port)准确说是五元组,还有一个是协议。但因为这里只是说TCP/IP协议,所以,这里我只说四元组。(有了这个src_ip和dst_ip,才保证了有TCP包有IP)
源端口和目的端口(Port)

各占 2 个 字节,共 4 个字节。
用来告知主机该报文段是来自哪里以及传送给哪个应用程序(应用程序绑定了端口)的。
进行 TCP 通讯时,客户端通常使用系统自动选择的临时端口号,而服务器则使用知名服务端口号。

序列号(Sequence Number)

占 4 个字节。
TCP 是面向字节流的,在一个 TCP 连接中传输的字节流中的每个字节都按照顺序编号。
例如 100 kb 的 HTML 文档数据,一共 102400 (100 * 1024) 个字节,那么每一个字节就都有了编号,整个文档的编号的范围是 0 ~ 102399。

序号字段值指的是本报文段所发送的数据的第一个字节的序号。
那么 100 kb 的 HTML 文档分割成四个等分之后,
第一个 TCP 报文段包含的是第一个 25kb 的数据,0 ~ 25599 字节, 该报文的序号的值就是:0
第二个 TCP 报文段包含的是第二个 25kb 的数据,25600 ~ 51199 字节,该报文的序号的值就是:25600

根据 8 位 = 1 字节,那么 4 个字节可以表示的数值范围:[0, 2^32],一共 2^32 (4294967296) 个序号。
序号增加到最大值的时候,下一个序号又回到了 0.
也就是说 TCP 协议可对 4GB 的数据进行编号,在一般情况下可保证当序号重复使用时,旧序号的数据早已经通过网络到达终点或者丢失了。

确认号(Acknowledgement Number)

占 4 个字节。
表示期望收到对方下一个报文段的序号值
TCP 的可靠性,是建立在「每一个数据报文都需要确认收到」的基础之上的。
就是说,通讯的任何一方在收到对方的一个报文之后,都要发送一个相对应的「确认报文」,来表达确认收到。
那么,确认报文,就会包含确认号
例如,通讯的一方收到了第一个 25kb 的报文,该报文的序号值=0,那么就需要回复一个确认报文,其中的确认号 = 25* 1024 = 25600,表示收到了25600字节之前的所有字节,下一个期待收到的报文段序号值为25600(第25600个字节开始)。

当ACK标志位为1时才有效。

数据偏移(Offset,首部长度)

占 0.5 个字节 (4 位二进制)。
这个字段实际上是指出了 TCP 报文段的首部长度 ,它指出了 TCP报文段的数据起始处 距离 TCP报文的起始处 有多远。(注意 数据起始处 和 报文起始处 的意思)

一个数据偏移量 = 4 byte(二进制位),由于 4 位二进制数能表示的最大十进制数字是 15,注意“数据偏移”的单位不是字节而是字,这里的1字=32bit=4字节,因此数据偏移的最大值是15字,也就是60字节,这也侧面限制了 TCP 首部的最大长度,由上图可知,没有任何选项字段的TCP头部长度为20字节。

保留位(Reserved)

占 0.75 个字节 (6 位)。
保留为今后使用,但目前应置为 0。

标志位(TCP Flags)

标志位,一共有 6 个,分别占 1 位,共 6 位 。
每一位的值只有 0 和 1,分别表达不同意思,下面来讲讲这6个位:

紧急 URG (Urgent)

当 URG = 1 的时候,表示紧急指针(Urgent Pointer)有效。
它告诉系统此报文段中有紧急数据,应尽快传送,而不要按原来的排队顺序来传送。
URG 要与首部中的 紧急指针 字段配合使用。

确认ACK(Acknowledgement)

当 ACK = 1 的时候,确认号(Acknowledgement Number)有效。
一般称携带 ACK 标志的 TCP 报文段为「确认报文段」。
TCP 规定,在连接建立后所有传送的报文段都必须把 ACK 设置为 1。

推送 PSH (Push)

当 PSH = 1 的时候,表示该报文段优先级高,接收方 (注意是接收方)TCP 应该尽快推送给接收应用程序,而不用等到整个 TCP 缓存都填满了后再交付。(联系一下OSI的各层)

复位 RST (Reset)

当 RST = 1 的时候,表示 TCP 连接中出现严重错误,需要释放并重新建立连接
一般称携带 RST 标志的 TCP 报文段为「复位报文段」。

同步 SYN (Synchronization)

当 SYN = 1 的时候,表明这是一个请求连接报文段。
一般称携带 SYN 标志的 TCP 报文段为「同步报文段」。
在 TCP 三次握手中的第一个报文就是同步报文段,在连接建立时用来同步序号。
对方若同意建立连接,则应在响应的报文段中使 SYN = 1 和 ACK = 1。

终止 FIN (Finis)

当 FIN = 1 时,表示此报文段的发送方的数据已经发送完毕,并要求释放 TCP 连接。
一般称携带 FIN 的报文段为「结束报文段」。
在 TCP 四次挥手释放连接的时候,就会用到该标志。

窗口大小(Window Size)

占 2 字节。
该字段明确指出了现在允许对方发送的数据量,它告诉对方本端的 TCP 接收缓冲区还能容纳多少字节的数据,这样对方就可以控制发送数据的速度。
窗口大小的值是指,从本报文段首部中的确认号算起,接收方目前允许对方发送的数据量。
例如,假如确认号是 701 ,窗口字段是 1000。这就表明,从 701 号算起,发送此报文段的一方还有接收 1000 (字节序号是 701 ~ 1700) 个字节的数据的接收缓存空间。

校验和(TCP Checksum)

占 2 个字节。
由发送端填充,接收端对 TCP 报文段执行 CRC 算法,以检验 TCP 报文段在传输过程中是否损坏,如果损坏这丢弃。
检验范围包括首部和数据两部分,这也是 TCP 可靠传输的一个重要保障

紧急指针 (Urgent Pointer)

占 2 个字节。
仅在 URG = 1 时才有意义,它指出本报文段中的紧急数据的字节数。
当 URG = 1 时,发送方 TCP 就把紧急数据插入到本报文段数据的最前面,而在紧急数据后面的数据仍是普通数据。
因此,紧急指针指出了紧急数据的末尾在报文段中的位置

紧急指针指向紧急数据最后一个字节的下一个字节,所以数据从序列号代表的字节到指针所指向字节(左闭右开)就是紧急数据。

紧急数据不进入缓冲区而是直接交付给上层进程。

TCP连接

在这里插入图片描述

三次握手,建立连接

TCP 连接建立要解决的首要问题就是:要使每一方能够确知对方的存在。

三次握手就像,在一个黑暗的森林,你知道前方十点钟方向好像有人。
你喊了一句:Hello?I’am JerryC,Who are you?
对面回了一句:Hi! I’am David, and nice to meet you!
然后你回了一句:Nice to meet you too!
…(自此,你们才算真正认识了双方,开始了后面省略3000字的谈话)

所以说,两个人需要交朋友(两个端点需要建立连接),至少需要三次的通话(握手)

其实,网络上的传输是没有连接的,TCP 也是一样的。
而 TCP 所谓的「连接」,其实只不过是在通信的双方维护一个「连接状态」,让它看上去好像有连接一样。

连接建立的过程
第一次握手

客户端主动打开连接,发送 TCP 报文,进行第一次握手,然后进入 SYN_SEND 状态,表示第一次握手的同步信号已经发送出去了,正在等待服务器发回确认报文。
这时首部的同步位 SYN = 1,同时初始化一个序号 Sequence Number = x,注意这个数据,后面会用到。

TCP 规定,此时的 SYN 报文段不能携带数据,但会消耗一个序号。

注意:此时的SYN,是TCP/IP建立连接时使用的握手信号,它只是TCP包首部的SYN位被标记为1,只有服务器端接收到了SYN=1的TCP包,才会与客户端建立连接关系,可以看成是一张相互信任的票。

第二次握手

服务器收到了 SYN 报文,如果同意建立连接,则向客户端发送一个确认报文,然后服务器进入 SYN_RCVD 状态。这时首部的 SYN = 1,ACK = 1,而确认号 Acknowledgement Number = x + 1,同时也为自己初始化一个序号Sequence Number = y。
这个报文也同样不携带数据,同时会消耗一个序号。

序号在TCP实现可靠数据传输起到了十分重要的作用,TCP 协议的通信双方, 都必须维护一个序列号, 以标识发送出去的数据包中, 哪些是已经被对方收到的。 三次握手的过程即是通信双方相互告知序列号起始值, 并确认对方已经收到了序列号起始值的必经步骤。

所以,如果第二次握手成功了,那么从客户端发起的序号就得到了确认,但是此时服务器端发起的序号y却只能在第三次握手才能被确认,这也是TCP为什么需要三次握手,两次不行的主要原因。

同时,服务器端发送了SYN+ACK报文后就会启动一个定时器,等待客户端返回的ACK确认包。如果第三次握手失败的话,也就是客户端给服务器端返回了ACK报文,服务器端并不能收到这个ACK报文。那么服务器端就会启动超时重传机制,超过规定时间后重新发送SYN+ACK,重传次数根据/proc/sys/net/ipv4/tcp_synack_retries来指定,默认是5次。如果重传指定次数到了后,仍然未收到ACK应答,那么一段时间后,服务器端将自动关闭这个连接。但是客户端认为这个连接已经建立如果客户端还向服务器端发数据,服务器端将以带RST标志的包响应(回顾前面的知识,当 RST = 1 的时候,表示 TCP 连接中出现严重错误,需要释放并重新建立连接)

第三次握手

客户端收到了服务器发过来的确认报文,还要向服务器给出确认,然后进入 ESTABLISHED 状态。
这时首部的 SYN 不再需要置为 1,而 ACK = 1,确认号 Acknowledgement Number = y + 1,序号 Sequence Number = x + 1。
第三次握手,标准来说,**TCP允许携带真正需要传输的数据,**当服务器收到该数据报文的时候,就会同样进入 ESTABLISHED 状态。 此时,TCP 连接已经建立。

有一种特殊情况,在TCP这三次握手中,只要接收到的数据包中存在SYN或FIN标志,就会会触发序列值增加1,这也是为什么第三次握手的序号仍为x+1的原因。

对于建立连接的三次握手,主要目的是初始化序号 Sequence Number,并且通信的双方都需要告知对方自己的初始化序号,所以这个过程也叫 SYN。
这个序号要作为以后的数据通信的序号,以保证应用层接收到的数据不会因为网络上的传输问题而乱序,因为TCP 会用这个序号来拼接数据。

半连接和全连接队列

在这里插入图片描述

当服务端调用listen函数监听端口的时候,内核会为每个监听的socket创建两个队列:

  • 半连接队列(syn queue):客户端发送SYN包,

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

是加油呀

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

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

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

打赏作者

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

抵扣说明:

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

余额充值