TCP/IP协议详解


TCP/IP 协议详解

一、TCP 端口

1. 端口与进程

TCP 的包是不包含 IP 地址信息的,那是 IP 层上的事,但是有源端口和目的端口。 就是说,端口这一东西,是属于 TCP 知识范畴的。
我们知道两个进程,在计算机内部进行通信,可以有管道、内存共享、信号量、消息队列等方法。 而两个进程如果需要进行通讯最基本的一个前提是能够唯一的标识一个进程,在本地进程通讯中我们可以使用「PID(进程标识符)」 来唯一标识一个进程。 但 PID 只在本地唯一,如果把两个进程放到了不同的两台计算机,然后他们要通信的话,PID 就不够用了,这样就需要另外一种手段了。
解决这个问题的方法就是在运输层使用「协议端口号 (protocol port number)」,简称「端口 (port)」。 我们知道 IP 层的 ip 地址可以唯一标识主机,而 TCP 层协议和端口号可以唯一标识主机的一个进程,这样我们可以利用:「ip 地址+协议+端口号」唯一标示网络中的一个进程。 在一些场合,也把这种唯一标识的模式称为「套接字 (Socket)」
这就是说,虽然通信的重点是应用进程,但我们只要把要传送的报文交到目的主机的某一个合适的端口,剩下的工作就由 TCP 来完成了。

2. 认识端口

TCP 用一个 16 位端口号来标识一个端口,可允许有 65536 ( 2 的 16 次方) 个不同的端口号,范围在 0 ~ 65535 之间。
一些常见的端口号:

应用程序 FTP TELNET SMTP DNS TFTP HTTP HTTPS SNMP
熟知端口号 21 23 25 53 69 80 443 161

二、TCP 报文结构

TCP 是面向字节流的,但传送的数据单元却是报文段。
什么是报文? 例如一个 100kb 的 HTML 文档需要传送到另外一台计算机,并不会整个文档直接传送过去,可能会切割成几个部分,比如四个分别为 25kb 的数据段。 而每个数据段再加上一个 TCP 首部,就组成了 TCP 报文。 一共四个 TCP 报文,发送到另外一个端。 另外一端收到数据包,然后再剔除 TCP 首部,组装起来。 等到四个数据包都收到了,就能还原出来一个完整的 HTML 文档了。
在 OSI 的七层协议中,第二层(数据链路层)的数据叫「Frame」,第三层(网络层)上的数据叫「Packet」,第四层(传输层)的数据叫「Segment」。
TCP 报文 (Segment),包括首部和数据部分:
实训二.png
而 TCP 的全部功能都体现在它首部中各字段的作用,只有弄清 TCP 首部各字段的作用才能掌握 TCP 的工作原理。 TCP 报文段首部的前 20 个字节是固定的,后面有 4N 字节是根据需要而增加的。 下图是把 TCP 报文中的首部放大来看。
image.png
TCP 的首部包括以下内容:

  1. 源端口 source port
  2. 目的端口 destination port
  3. 序号 sequence number
  4. 确认号 acknowledgment number
  5. 数据偏移 offset
  6. 保留 reserved
  7. 标志位 tcp flags
  8. 窗口大小 window size
  9. 检验和 checksum
  10. 紧急指针 urgent pointer
  11. 选项 tcp options

TCP 首部各字段的意义和作用

01. 源端口和目的端口 Port

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

02. 序号 Sequence Number

4 个字节。 TCP 是面向字节流的,在一个 TCP 连接中传输的字节流中的每个字节都按照顺序编号。 例如 100 kb 的 HTML 文档数据,一共 102400 (100 * 1024) 个字节,那么每一个字节就都有了编号,整个文档的编号的范围是 0 ~ 102399。
序号字段值指的是本报文段所发送的数据的第一个字节的序号。 那么 100 的 HTML 文档分割成四个等分之后, 第一个 TCP 报文段包含的是第一个 25kb 的数据,0 ~ 25599 字节, 该报文的序号的值就是:0 第二个 TCP 报文段包含的是第二个 25kb 的数据,25600 ~ 51199 字节,该报文的序号的值就是:25600 ……
根据 8 位 = 1 字节,那么 4 个字节可以表示的数值范围:[0, 2^32],一共 2^32 (4294967296) 个序号。 序号增加到最大值的时候,下一个序号又回到了 0. 也就是说 TCP 协议可对 4GB 的数据进行编号,在一般情况下可保证当序号重复使用时,旧序号的数据早已经通过网络到达终点或者丢失了。

03. 确认号 Acknowledgemt Number

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

04. 数据偏移 Offset

0.5个字节 (4 位)。 这个字段实际上是指出了TCP 报文段的首部长度,它指出了 TCP 报文段的数据起始处 距离 TCP 报文的起始处 有多远。(注意 数据起始处 和 报文起始处 的意思)
一个数据偏移量 = 4 byte,由于 4 位二进制数能表示的最大十进制数字是 15,因此数据偏移的最大值是 60 byte,这也侧面限制了 TCP 首部的最大长度。

05. 保留 Reserved

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

06. 标志位 TCP Flags

标志位,一共有 6 个,分别占 1 位,共 6 位 。 每一位的值只有 0 和 1,分别表达不同意思。
紧急 URG (Urgent)
URG = 1 的时候,表示紧急指针(Urgent Pointer)有效。 它告诉系统此报文段中有紧急数据,应尽快传送,而不要按原来的排队顺序来传送。 URG 要与首部中的 紧急指针 字段配合使用。
确认 ACK (Acknowlegemt)
ACK = 1 的时候,确认号(Acknowledgemt Number)有效。 一般称携带 ACK 标志的 TCP 报文段为「确认报文段」。 TCP 规定,在连接建立后所有传送的报文段都必须把 ACK 设置为 1。
推送 PSH (Push)
PSH = 1 的时候,表示该报文段高优先级,接收方 TCP 应该尽快推送给接收应用程序,而不用等到整个 TCP 缓存都填满了后再交付。
复位 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 四次挥手释放连接的时候,就会用到该标志。

07. 窗口大小 Window Size

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

08. 校验和 TCP Checksum

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

09. 紧急指针 Urgent Pointer

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

三、TCP 连接的建立和释放

1. TCP 的连接

TCP 的整个交流过程可以总结为:先建立连接,然后传输数据,最后释放连接。
实训二 (1).png
详细过程:
image.jpeg

2、三次握手,建立连接

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 连接的建立采用客户服务器方式,主动发起连接建立的一方叫客户端(Client),被动等待连接建立的一方叫服务器(Server)

最初的时候,两端都处于CLOSED的状态,然后服务器打开了 TCP 服务,进入LISTEN状态,监听特定端口,等待客户端的 TCP 请求。

第一次握手: 客户端主动打开连接,发送 TCP 报文,进行第一次握手,然后进入 SYN_SEND 状态,等待服务器发回确认报文。 这时首部的同步位 SYN = 1,同时初始化一个序号 Sequence Number = J。 TCP 规定,SYN 报文段不能携带数据,但会消耗一个序号。

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

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

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

利用连接设计缺陷实施 TCP Flood 攻击

知道了 TCP 建立一个连接,需要进行三次握手。
但如果你开始思考「三次握手的必要性」的时候,就会知道,其实网络是很复杂的,一个信息在途中丢失的可能性是有的。
如果数据丢失了,那么,就需要重新发送,这时候就要知道数据是否真的送达了。 这就是三次握手的必要性。
但是再向深一层思考,你给我发信息,我收到了,我回复,因为我是君子。
如果是小人,你给我发信息,我就算收到了,我也不回复,你就一直等我着我的回复。
那么很多小人都这样做,你就要一直记住你在等待着小人 1 号、小人 2 号、小人 3 号……直到你的脑容量爆棚,烧坏脑袋。
黑客就是利用这样的设计缺陷,实施 TCP Flood 攻击,属于 DDOS 攻击的一种。

3、四次挥手,释放连接

TCP 有一个特别的概念叫做半关闭,这个概念是说,TCP 的连接是全双工(可以同时发送和接收)的连接,因此在关闭连接的时候,必须关闭传送和接收两个方向上的连接。 客户端给服务器发送一个携带 FIN 的 TCP 结束报文段,然后服务器返回给客户端一个 确认报文段,同时发送一个 结束报文段,当客户端回复一个 确认报文段 之后,连接就结束了。

释放连接过程

在结束之前,通信双方都是处于 ESTABLISHED 状态,然后其中一方主动断开连接。 下面假如客户端先主动断开连接。

第一次挥手:客户端向服务器发送结束报文段,然后进入FIN_WAIT_1状态。 此报文段 FIN = 1, Sequence Number = M。

第二次挥手:服务端收到客户端的结束报文段,然后发送确认报文段,进入CLOSE_WAIT状态。 此报文段 ACK = 1, Sequence Number = M + 1。
客户端收到该报文,会进入 FIN_WAIT_2 状态。

第三次挥手:同时服务端向客户端发送结束报文段,然后进入LAST_ACK状态。 此报文段 FIN = 1,Sequence Number = N。

第四次挥手:客户端收到服务端的结束报文段,然后发送确认报文段,进入TIME_WAIT状态,经过 2MSL 之后,自动进入CLOSED状态。 此报文段 ACK = 1, Sequence Number = N + 1。

服务端收到该报文之后,进入 CLOSED 状态。

关于 TIME_WAIT 过渡到 CLOSED 状态说明: 从TIME_WAIT进入CLOSED需要经过 2MSL,其中 MSL 就叫做 最长报文段寿命(Maxinum Segment Lifetime),根据 RFC 793 建议该值这是为 2 分钟,也就是说需要经过 4 分钟,才进入CLOSED状态。

四、TCP 状态流转

无论客户端还是服务器,在双方 TCP 通讯的过程中,都会有着一个「状态」的概念,状态会随着 TCP 通讯的不同阶段而变化。

1. TCP 状态流转图

image (1).jpeg

2. 各种状态表示的意思

CLOSED:表示初始状态
LISTEN:表示服务器端的某个 socket 处于监听状态,可以接受连接
SYN_SENT:在服务端监听后,客户端 socket 执行 CONNECT 连接时,客户端发送 SYN 报文,此时客户端就进入 SYN_SENT 状态,等待服务端确认。
SYN_RCVD:表示服务端接收到了 SYN 报文。
ESTABLISHED:表示连接已经建立了。
FIN_WAIT_1:其中一方请求终止连接,等待对方的 FIN 报文。
FIN_WAIT_2:在FIN_WAIT_2 之后, 当对方回应 ACK 报文之后,进入该状态。
TIME_WAIT:表示收到了对方的 FIN 报文,并发送出了 ACK 报文,就等 2MSL 之后即可回到 CLOSED 状态。
CLOSING:一种罕见状态,发生在发送 FIN 报文之后,本应是先收到 ACK 报文,却先收到对方的 FIN 报文,那么就从 FIN_WAIT_1 的状态进入 CLOSING 状态。
CLOSE_WAIT:表示等待关闭,在 ESTABLISHED 过渡到 LAST_ACK 的一个过渡阶段,该阶段需要考虑是否还有数据发送给对方,如果没有,就可以关闭连接,发送 FIN 报文,然后进入 LAST_ACK 状态。
LAST_ACK:被动关闭一方发送 FIN 报文之后,最后等待对方的 ACK 报文所处的状态。
CLOSED:当收到 ACK 保温后,就可以进入 CLOSED 状态了。


文章作者: Truda
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Truda !
评论
  目录