355 lines
29 KiB
Markdown
355 lines
29 KiB
Markdown
#unity/日常积累
|
||
|
||
![[Pasted image 20240415134014.png]]
|
||
|
||
在世界上各地,各种各样的电脑,运行着各自不同的操作系统为大家服务。这些电脑,在表达同一种信息的时候,所使用的方法是千差万别。
|
||
|
||
计算机使用者意识到,计算机只是单兵作战,并不会发挥太大的作用。只有把它们联合起来,电脑才会发挥出它最大的潜力。
|
||
|
||
于是人们就想方设法的,用电线把电脑连接到了一起。但是简单的连到一起是远远不够的,就好像语言不同的两个人互相见了面,完全不能交流信息。因而他们需要定义一些共通的东西来进行交流,TCP/IP 就是为此而生。
|
||
|
||
TCP/IP 不是一个协议,而是一个协议族的统称。里面包括了 IP 协议,IMCP 协议,TCP 协议,以及我们更加熟悉的 http、ftp 协议等等。电脑有了这些,就好像学会了外语一样,就可以和其他的计算机终端做自由的交流了。今天我们学习 TCP 协议。
|
||
|
||
## 什么是TCP
|
||
|
||
TCP(传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议,它完成第四层传输层所指定的功能,网络模型下面介绍。
|
||
|
||
TCP 协议的特点是:
|
||
|
||
![[Pasted image 20240415134025.png]]
|
||
|
||
- _**面向连接**_:一定是「一对一」才能连接,不能像 UDP 协议可以一个主机同时向多个主机发送消息,也就是一对多是无法做到的。
|
||
- _**可靠交付**_:无论的网络链路中出现了怎样的链路变化,TCP 都可以保证一个报文一定能够到达接收端。
|
||
- _**面向字节流**_:也就是说仅仅把上层协议传递过来的数据当成字节传输。
|
||
|
||
## 网络模型
|
||
|
||
#### 七层模型
|
||
|
||
国际标准化组织 ISO ,在1981 年正式推荐了一个网络系统结构一七层参考模型,也叫作开放系统互连模。由于这个标准模型的建立,使得各种计算机网络均向它靠拢,大大推动了网络通信的发展。
|
||
|
||
这个 ISO 层网络模型各层的名字、主要功能对应的典型设备和传输单位如下图:
|
||
|
||
![[Pasted image 20240415134033.png]]
|
||
|
||
这个七层网络模型在数据的传输过程中还会对数据进行封装,如下图:
|
||
|
||
![[Pasted image 20240415134041.png]]
|
||
|
||
ISO 层网络模型中,当一台主需要传送用户的数据 (data) 时,数据首先通过应用层的接口进入应用层。
|
||
|
||
先看几个常见报头术语简写:
|
||
|
||
- 应用层报头:_Ppplication Header_, 简称 _AH_。
|
||
- 表示层报头:_Presentation Header_, 简称 _PH_。
|
||
- 会话层报头:_Session Header_, 简称 _SH_。
|
||
- 传输层报头:_Transport Header_, 简称 _TH_。
|
||
- 网络层报头:_Network Header_, 简称 _NH_。
|
||
- 数据链路层报头:_Data link Header_, 简称 _DH_。
|
||
- 应用层协议数据单元:_Protocol Data Unit_,简称 _PDU_。
|
||
- 数据链路层报尾:_Data link Termination_,简称 _DT_。
|
||
|
||
在应用层,用户的数据被加上应用层的报头 AH,形成应用层协议数据单元 PDU,然后被递交到下层表示层。
|
||
|
||
表示层并不关心上层应用层的数据格式,而是把整个应用层递交的数据包,看成是一个整体进行封装,即加上表示层的报头 PH。然后,递交到下层会话层。
|
||
|
||
同样,会话层、传输层、网络层(假设用 TCP 传输,则是 TCP 数据+ IP 包头)、数据链路层(把上层的 TCP 数据+ IP 头统一称为帧数据,即帧 +帧数据+帧尾(CRC)也都要分别给上层递交下来的数据加上自己的报头)。
|
||
|
||
它们是:会话层报头 SH、传输层报头 TH、网络层报头 NH 和数据链路层报头DH。其中,数据链路层还要给网络层递交的数据加上数据链路层报尾形成最终的一帧数据。
|
||
|
||
当一帧数据,通过物理层传送到目标主机的物理层时,该主机的物理层把它递交到上层一一数据链路层。数据链路层负责去掉数据帧的帧头部和尾部(同时还进行数据校验)。如果数据没有出错,则递交到上层网络层。
|
||
|
||
同样,网络层、传输层、会话层、表示层、应用层也要做类似的工作。最终 ,原始数据被递交到目标主机的具体应用程序中。
|
||
|
||
### 五层网络模型
|
||
|
||
五层模型的网络体系也经常被提到,这五层的名字与功能分别如下所述:
|
||
|
||
- _**应用层**_:确定进程之间通信的性质,以满足用户需求。应用层协议有很多。如支持万维网应用的 HTTP 协议、支持电子邮件的 SMTP 协议、等等。
|
||
- _**传输层**_:负责主机间不同进程的通信。这一层中的协议有面向连接的 TCP (传输控制协议)、无连接的 UDP (用户数据报协议);数据传输的单位称为报文段或用户数据报。
|
||
- _**网络层**_:负责分组交换网中不同主机间的通信。作用为:发送数据时,将运输层中的报文段或用户数据报封装成 IP 数据报,并选择合适路由。
|
||
- _**数据链路层**_:负责将网络层的 IP 数据报组装成帧。
|
||
- _**物理层**_ :透明地传输比特流。
|
||
|
||
#### 四层网络模型
|
||
|
||
前面的两种模型都是学术上的概念,使用并不广泛 还有一个四层模型,使用最为广泛一 TCP/IP 分层模型。几种模型如下图:
|
||
|
||
![[Pasted image 20240415134102.png]]
|
||
|
||
TCP/IP 分层的四模型的个协议层分别完成以下的功能:
|
||
|
||
- _**网络接口层**_:包括用于协作 IP 数据,在已有网络介质上传输的协议。实际上 TCP/IP 标准并不定义与 ISO 数据链路层和物理层相对应的功能 。相反,它定义了像 ARP (地址解析协议)这样的协议,提供 TCP/IP 协议的数据结构和实际物理硬件之间的接口。
|
||
- _**网络层**_:网络层对应于 OSI 七层参考模型的网络层。本层包含 IP 协议、RIP 协议(路由信息协议),负责数据的包装、寻址和路由。同时还包含 ICMP (网间控制报文协议)用来提供网络诊断信息。
|
||
- _**传输层**_:传输层对应于 OSI 七层参考模型的传输层,它提供两种端到端的通信服务。其中 TCP协议提供可靠的数据流运输服务, UDP 协议提供不可靠的用户数据报服务。
|
||
- _**应用层**_:应用层对应于 OSI 七层参考模型的应用层和表示层。因特网的应用层协议包括 FTP (文件传输协议)HTTP (超文本传输协议)、 Telent (远程终端协议)、SMTP (简单邮件传送协议)、 IRC (因特网中继会话)、NNTP (网络新闻传输协议)等。
|
||
|
||
综上所述,我们需要知道 TCP 协议在网络 OSI 的七层模型中的第四层传输层, IP 协议在第三层网络层, ARP 协议在第二层数据链路层;在第二层上的数据叫 Frame ,在第三层上的数据叫 Packet ,第四层的数据叫 Segment 所有程序的数据首先会打包到 TCP 的 Segment 中。
|
||
|
||
然后 TCP 的 Segment 会打包到 IP 的 Packet ,然后再打包到以太网 Ethernet 的 Frame 中,传到对端后,各个 解析自己的协议,然后把数据交给更高层的协议处理。
|
||
|
||
## TCP头格式
|
||
|
||
在学习 TCP 连接之前,还要学习一下 TCP 头部格式。因为 TCP 连接建立,需要用 TCP 包来交换和管理数据,下面看一下 TCP 头部格式。
|
||
|
||
![[Pasted image 20240415134113.png]]
|
||
|
||
TCP 头部里每个字段都为管理 TCP 连接和控制数据流起了重要作用。
|
||
|
||
**16 位端口号**:告知主机该报文段是来自哪里(源端口)以及传给哪个上层协议或应用程序(目的端口)的。
|
||
|
||
进行 TCP 通信时,客户端通常使用系统自动选择的临时端口号,而服务器则使用知名服务端口号。所有知名服务使用的端口号都定义在 /etc/services 文件中。
|
||
|
||
**32 位序号**(sequence number):一次 TCP 通信(从 TCP 连接建立到断开)过程中个传输方向上的字节流的每个字节的编号。
|
||
|
||
**32 位确认号**(acknowledgement number):用作对另一方发送来的 TCP 报文段的响应其值是收到的 TCP 报文段的序号值加 1。
|
||
|
||
**4 位头部长度**(header length):标识该 TCP 头部有多少个 32bit ( 4 Byte 因为最大能表示 15 ,所以 TCP 头部最长是 60 Byte。
|
||
|
||
**6 位标志**位包含如下几项:
|
||
|
||
- _URG_ 标志,表示紧急指针是否有效。
|
||
- _ACK_ 标志,表示确认号是否有效,一般称携带 ACK 标志的 TCP 报文段为"确认报文段"。
|
||
- _PSH_ 标志,提示接收端应用程序应该立即从 TCP 接收缓冲区中读走数据,为接收后续数据腾出空间(如果应用程序不将接收到的数据读走,它们就会直停留在 TCP 接收缓冲区中)。
|
||
- _RST_ 标志,表示要求对方重新建立连接,一般称携带 RST 标志的 TCP 报文段为"复位报文段"。
|
||
- _SYN_ 标志,表示请求建立 个连接,一般称携带 SYN 标志的 TCP 报文段为"同步报文段"。FIN 标志,表示通知对方本端要关闭连接了,一般称携带 FIN 标志的 TCP 报文段为"结束报文段"。
|
||
|
||
**16 位窗口大小**(window size):是 TCP 流量控制的一个手段。这里说的窗口,指的是接收通告窗口( Receiver Window, RWND)。它告诉对方本端的 TCP 接收缓冲区还能容纳多少字节的数据,这样对方就可以控制发送数据的速度。
|
||
|
||
**16 位校验和**(TCP checksum):由发送端填充,接收端对 TCP 报文段执行 CRC 算法,以检验 TCP 报文段在传输过程中是否损坏 。注意,这个校验不仅包括 TCP 头部,也包括数据部分。这也是 TCP 可靠传输的一个重要保障。
|
||
|
||
**16 位紧急指针**(urgent pointer):是一个正的偏移量。它和序号字段的值相加表示最后一个紧急数据的下一字节的序号 。确切地说,这个字段是紧急指针相对当前序号的偏移,不妨称之为“紧急偏移”。TCP 的紧急指针是发送端向接收端发送紧急数据的方法。
|
||
|
||
综上,你需要注意如下几点:
|
||
|
||
TCP 的包是没有 IP 地址的,那是 IP 层上的事,但是有源端口和目的端口。
|
||
|
||
一个TCP 连接需要四元组( src_ip,src_port,dst_ip,dst_port )来表示是同一个连接 准确说是五元组,还有一个是协议 但因为这里只是强调 TCP 协议,所以,只说四元组。
|
||
|
||
![[Pasted image 20240415134123.png]]
|
||
|
||
_Sequence Number_ 是包的序号,用来解决网络包乱序(reordering )问题。
|
||
|
||
_Acknowledgement Number_ 就是 ACK ,用于确认收到,用来解决不丢包的问题。
|
||
|
||
_Window Advertised Window_ ,也就是著名 的滑动窗口 Sliding Window ),用于解决流量控制问题。
|
||
|
||
_TCP Flag_ ,也就是包的类型,主要是用于操控 TCP 的状态机的。
|
||
|
||
## TCP三次握手
|
||
|
||
其实,网络上的传输是没有连接的, TCP 是一样的 TCP 所谓的 “连接”,其实只不过是在通信的双方维护一个“连接状态”,让它看上去好像有连接一样 所以, TCP 的状态变换是非常重要的 。
|
||
|
||
先来看一下著名的三次握手图。
|
||
|
||
![[Pasted image 20240415134131.png]]
|
||
|
||
TCP 连接的建立可以简单地称为三次握手,而连接的中止则可以称为四次挥手。
|
||
|
||
建立连接 TCP/IP 协议中, TCP 协议提供可靠的连接服务,采用三次握手建立一个连接。
|
||
|
||
- _**第一次握手**_:建立连接时,客户端发送 SYN 包到服务器,并进入 SYN_SEND 状态,等待服务器确认。
|
||
|
||
- _**第二次握手**_:服务器收到 SYN 包,必须确认客户的 SYN ,同时自己也发送一个 SYN 包,即 SYN+ACK 包,此时服务器进入 SYN_RECV 状态。
|
||
|
||
- _**第三次握手**_ :客户端收到服务器的 SYN + ACK 包,向服务器发送确认包 ACK,此包发送完毕,客户端和服务器进入 ESTABLISHE 态。
|
||
|
||
|
||
完成 三次握手,客户端与服务器开始传送数据,也就是 ESTABLISHED 状态。
|
||
|
||
## 连接建立中的异常
|
||
|
||
#### 建连接时SYN超时问题
|
||
|
||
如果 server 端因为某种情况没有收到 client 回来的 ACK,那么,这个连接处还处于一个未建立的状态。于是,server端如果在一定时间内没有收到,则 server 端的 TCP 会重发 SYN_ACK。
|
||
|
||
在Linux下,默认重试次数为5次,重试的间隔时间从1s开始每次都翻倍,5次的重试时间间隔为1s, 2s, 4s, 8s, 16s,总共31s,第5次发出后还要等32s都知道第5次也超时了。如果第五次重传之后,还未收到客户端的 ACK,server 端的 TCP 才会把断开这个连接。
|
||
|
||
#### 关于SYN Flood攻击
|
||
|
||
攻击者短时间伪造不同 IP 地址的 SYN 报文,服务端每接收到一个 SYN 报文,就进入SYN_RCVD 状态,但服务端发送出去的 ACK + SYN 报文,无法得到未知 IP 主机的 ACK 应答,久而久之就会占满服务端的 SYN 接收队列(未连接队列),使得服务器不能为正常用户服务。
|
||
|
||
#### 避免方式
|
||
|
||
设置 _tcp_syncookies = 1_。当 SYN 队列满了后,TCP 会通过源地址端口、目标地址端口和时间戳打造出一个特别的 Sequence Number 发回去(又叫cookie)。
|
||
|
||
如果是攻击者则不会有响应,如果是正常连接,则会把这个 SYN Cookie 发回来,然后服务端可以通过 cookie 建连接。
|
||
|
||
设置 _netdev_max_backlog_ 的值,确定链接队列的大小。当网卡接收数据包的速度大于内核处理的速度时,会有一个队列保存这些数据包。
|
||
|
||
通过设置 _netdev_max_backlog_ 的值,确定 SYN_RCVD 状态连接的最大个数。
|
||
|
||
通过设置 _tcp_abort_on_overflow_ 的值。当超出处理能时,对新的 SYN 直接回报 RST,丢弃连接。
|
||
|
||
## TCP四次挥手
|
||
|
||
![[Pasted image 20240415134146.png]]
|
||
|
||
#### TCP的连接断开
|
||
|
||
TCP 一个特别的概念叫作半关闭,这个概念是说, TCP 的连接是全双工(可以同时发送和接收)连接,因此在关闭连接的时候,必须关闭传和送两个方向上的连接。
|
||
|
||
客户机给服务器 FIN 的 TCP 报文,然后服务器返回给客户端一个确认 ACK 报文,并且发送一个FIN 报文,当客户机回复 ACK 报文后( 四次握手),连接就结束了。
|
||
|
||
在建立连接的时候,通信的双方要互相确认对方的最大报文长度( MSS ),以便通信。
|
||
|
||
一般这个 SYN 长度是 MTU 减去固定 IP 首部和 TCP 首部长度。对于一个以太网,一般可以达 1460 Byte 。当然如果对于非本地的 IP ,这个 MSS 可能就只有 536 Byte ,而且,如果中间的传输网络的 MSS 更加的小的话,这个值还会变得更小。
|
||
|
||
#### 为什么建连接要三次握手,断连接需要四次挥手?
|
||
|
||
对于建连接的三次握手,主要是要初始化 Sequence Number 的初始值。通信的双方要互相通知对方自己的初始化的 Sequence Numbe,所以叫 SYN 。
|
||
|
||
这个号要作为以后的数据通信的序号,以保证应用层接收到的数据不会因为网络上的传输问题而乱序( TCP 会用这个序号来拼接数据)。
|
||
|
||
对于四次挥手,其实仔细看则是两次,因为 TCP 是全双工的,所以,发送方和接收方都需要 FIN 和 ACK。
|
||
|
||
只不过,有一方是被动的,所以看上去就成了所谓的四次挥手 。如果两边同时断连接,那就会就进入到 CLOSING 状态,接着就是TIME_WAIT 状态。
|
||
|
||
## 断开连接中的异常
|
||
|
||
#### TIME_WAIT数量太多
|
||
|
||
从上面的描述可以知道,TIME_WAIT 是个很重要的状态,但是如果在大并发的短链接下,TIME_WAIT 就会太多。TIME_WAIT过多会占用大量的内存资源和端口资源。
|
||
|
||
优化法一:tcp_tw_reuse
|
||
|
||
设置tcp_tw_reuse = 1,则可以复用处于 TIME_WAIT 的 socket 为新的连接所用。
|
||
|
||
有一点需要注意的是,tcp_tw_reuse 功能只能用客户端(连接发起方),因为开启了该功能,在调用 connect() 函数时,内核会随机找一个 time_wait 状态超过 1 秒的连接给新的连接复用。
|
||
|
||
使用 tcp_timestamps = 1 选项,还有一个前提,需要打开对 TCP 时间戳的支持,即这个时间戳的字段是在 TCP 头部的「选项」里,用于记录 TCP 发送方的当前时间戳和从对端接收到的最新时间戳。
|
||
|
||
由于引入了时间戳,我们在前面提到的 2MSL 问题就不复存在了,因为重复的数据包会因为时间戳过期被自然丢弃。
|
||
|
||
优化法二:tcp_max_tw_buckets
|
||
|
||
这个值默认为 18000,当系统中处于 TIME_WAIT 的连接一旦超过这个值时,系统就会将后面的 TIME_WAIT 连接状态重置。
|
||
|
||
这个方法过于暴力,而且治标不治本,带来的问题远比解决的问题多,不推荐使用。
|
||
|
||
## TCP状态流转
|
||
|
||
接下来再看一下著名的 TCP 状态流转图。
|
||
|
||
![[Pasted image 20240415134155.png]]
|
||
|
||
**CLOSED状态**:表示初始状态。
|
||
|
||
**LISTEN状态**:表示服务器端的某个 socket 处于监听状态,可以接受连接。
|
||
|
||
**SYN_SENT状态**:在服务端监听后,客户端 socket 执行 CONNECT 连接时,客户端发送 SYN 报文,此时客户端就进入 SYN_SENT 状态,等待服务端的确认。
|
||
|
||
**SYN_RCVD状态**:表示服务端接收到了SYN 报文,在正常情况下,这个状态是服务器端的 socket 在建立 TCP 连接时的三次握手会话过程中的一个中间状态,很短暂,基本上用网络查询工具 netstat 是很难看到这种状态的。因此这种状态时,当收到客户端的 ACK 报文后,它会进入到 ESTABLISHED 状态。
|
||
|
||
**ESTABLISHED状态**:表示连接已经建立了。
|
||
|
||
**FIN_WAIT_1状态**:这个是已经建立连接之后,其中一方请求终止连接,等待对方的 FIN 报文 。
|
||
|
||
FIN_WAIT_1 状态是当 socket 在 ESTABLISHED 状态时,它想主动关闭连接,向对方发送了 FIN 报文,此时该 socket 即进入到 FIN_WAIT_1 状态。而当对方回应 ACK 报文后,则进入到 FIN_WAIT_2 状态。
|
||
|
||
当然在实际的正常情况下,无论对方处于何种情况,都应该马上回应 ACK 报文,所以 FIN_WAIT_1 状态一般是比较难见到的,而 FIN_WAIT_2 状态还可以用 netstat 看到。
|
||
|
||
**FIN_WAIT_2状态**:实际上 FIN_WAIT_2 状态下的 socket ,表示半连接,即有一方要求关闭连接,但另外还告诉对方:我暂时还有点数据需要传送给你,请稍后再关闭连接。
|
||
|
||
**TIME_ WAIT状态**:表示收到了对方的 FIN 报文,并发送出了 ACK 报文,就等 2MSL 后即可回到 CLOSED 可用状态了。如果在 FIN_WAIT_1 状态下,收到了对方同时带 FIN 标志和 ACK 标志的报文时,可以直接进入到 TIME_WAIT 状态,而无需经过 FIN_WAIT_2 状态。
|
||
|
||
**CLOSING状态**:这种状态比较特殊,实际情况中应该是很少见。正常情况下,当发送 FIN 报文后,按理来说是应该先收到(或同时收到)对方的ACK 报文,再收到对方的 FIN 报文 。但是 CLOSING 状态表示你发送 FIN 报文后,并没有收到对方的 ACK 报文,反而收到了对方的 FIN 报文 。
|
||
|
||
如果双方几乎在同时关闭一个 socket 的话,那么就出现了双方同时发送 FIN 报文的情况,就会出现 CLOSING 状态,表示双方都正在关闭 socket 连接。
|
||
|
||
**CLOSE_WAIT状态**:表示在等待关闭。当对方关闭一个 socket 后发送 FIN 报文给自己时,系统将毫无疑问地会回应 ACK 报文给对方,此时则进入到 CLOSE_WAIT 状态。
|
||
|
||
接下来呢,实际上你真正需要考虑的事情是察看你是否还有数据发送给对方,如果没有,那么你也就可以关闭这个socket了,发送 FIN 报文给对方,即关闭连接 。CLOSE _WAIT 状态下,需要完成的事情是等待你去关闭连接。
|
||
|
||
**LAST_ACK状态**:这个状态还是比较好理解的,它是被动关闭 方在发送 FIN 报文后,最后等待对方的 ACK 报文。
|
||
|
||
**CLOSED状态**:当收到 ACK 报文后,也即可以进入到 CLOSED 可用状态了。
|
||
|
||
**2MSL 等待状态**:在 FIN_WAIT_2 发送了最后一个 ACK 数据报以后,要进入 TIME_WAIT 态,这个状态是防止最后一次握手的数据报没有传送到对方那里而准备的。
|
||
|
||
由于 socket 2MSL 状态,使得应用程序在 2MSL 时间内无法再次使用同一个 socket ,对于客户程序还好 些,但是对于服务程序(httpd),它总是要使用同一个端口来进行服务,而在 2MSL 时间内,启动 httpd 就会出现错误(插口被使用)。
|
||
|
||
为了避免这个错误,服务器给出了一个平静时间的概念,这是说在 2MSL的时间内,虽然可以重新启动服务器,但是这个服务器还是要平静地等待 2MSL 的时间才能进行下一次连接。
|
||
|
||
**FIN WAIT_2 状态**:这就是著名的半关闭状态了,这是在关闭连接时,客户端和服务器两次握手之后的状态 。
|
||
|
||
在这个状态下,应用程序还有接收数据的能力。已经无法发送数据,但是也有一种可能是,客户端处于FIN_WAIT_2 状态,而服务器则一直处于 WAIT_CLOSE 状态,直到应用层来决定关闭这个状态。
|
||
|
||
**RST 同时打开和同时关闭**:RST 是另一种关闭连接的方式,应用程序应该可以判断RST 包的真实性,即是否为异常中止 而同时打开和同时关闭则是两种特殊的 TCP 状态,发生的概率很小。
|
||
|
||
## 总结
|
||
|
||
本文主要讲述了网络分层模型,以及各层的作用,数据包是怎么组装和拆包的。TCP 包结构也大致学习了下,还有 TCP 连接的建立和断开。
|
||
|
||
TCP 连接建立之后才开始发数据包,所以 TCP 三次握手很重要。TCP 三次握手中也可能存在一些异常,只有彻底搞懂三次握手才能正确处理这些异常。
|
||
|
||
TCP 四次挥手也很重要,server 中经常要接受和断开连接。对应断开连接中的异常,以及服务器请求量过多,只有在搞懂 TCP 四次挥手以后,处理这些问题才能得心应手。
|
||
|
||
|
||
以下就是TCP字节流服务和UDP数据报服务的具体框图:(TCP多对多,UDP一对一)
|
||
|
||
![[Pasted image 20240415134321.png]]
|
||
|
||
## TCP头部结构
|
||
|
||
以下是TCP头部结构:
|
||
|
||
![[Pasted image 20240415134255.png]]
|
||
|
||
16位端口号:告知主机该报文段是来自哪里(源端口Source Port)以及传给哪个上层协议或应用程序(目的端口Destination Port)的。进行TCP通信时,客户端通常使用系统自动选择的临时端口号,而服务器则使用知名服务端口号(比如DNS协议对应端口53,HTTP协议对应80,这些端口号可在/etc/services文件中找到)。
|
||
|
||
32位序号:一次TCP通信(从TCP连接建立到断开)过程中某一个传输方向上的字节流的每个字节的编号。假设主机A和主机B进行TCP通信,A发送给B的第一个TCP报文段中,序号值被系统初始化为某个随机值ISN(Initial Sequence Number,初始序号值)。那么在该传输方向上(从A到B),后续的TCP报文段中序号值将被系统设置成ISN加上该报文段所携带数据的第一个字节在整个字节流中的偏移。例如,某个TCP报文段传送的数据是字节流中的第1025~2048字节,那么该报文段的序号值就是ISN+1025.另外一个传输方向(从B到A)的TCP报文段的序号值也具有相同的含义。
|
||
|
||
32位确认号(acknowledgement number):用作对另一方发送来的TCP报文段的响应。其值是收到的TCP报文段的序号值加1。假设主机A和主机B进行TCP通信,那么A发送出的TCP报文段不仅携带自己的序号,而且包含对B发送来的TCP报文段的确认号。反之,B发送出的TCP报文段也同时携带自己的序号和对A发送来的报文段的确认号。
|
||
|
||
4位头部长度(header length):标识该TCP头部有多少个32bit字(4字节)。因为4位最大能标识15,所以TCP头部最长是60字节。
|
||
|
||
6位标志位包含如下几项:
|
||
|
||
URG标志,表示紧急指针(urgent pointer)是否有效。
|
||
|
||
ACK标志,表示确认号是否有效。我们称携带ACK标识的TCP报文段为确认报文段。
|
||
|
||
PSH标志,提示接收端应用程序应该立即从TCP接收缓冲区中读走数据,为接收后续数据腾出空间(如果应用程序不将接收
|
||
|
||
到的数据读走,它们就会一直停留在TCP接收缓冲区中)。
|
||
|
||
RST标志,表示要求对方重新建立连接。我们称携带RST标志的TCP报文段为复位报文段。
|
||
|
||
SYN标志,表示请求建立一个连接。我们称携带SYN标志的TCP报文段为同步报文段。
|
||
|
||
FIN标志,表示通知对方本端要关闭连接了。我们称携带FIN标志的TCP报文段为结束报文段。
|
||
|
||
16位窗口大小(window size):是TCP流量控制的一个手段。这里说的窗口,指的是接收通告窗口(Receiver Window,RWND)。它告诉对方本端的TCP接收缓冲区还能容纳多少字节的数据,这样对方就可以控制发送数据的速度。
|
||
|
||
16位校验和(TCP check sum):由发送端填充,接收端对TCP报文段执行CRC算法以检验TCP报文段在传输过程中是否损坏。注意,这个校验不仅包括TCP头部,也包括数据部分。这也是TCP可靠传输的一个重要保障。
|
||
|
||
16位紧急指针(urgent pointer):是一个正的偏移量。它和序号字段的值相加表示最后一个紧急数据的下一字节的序号。因此,确切地说,这个字段是紧急指针相对当前序号的偏移,不妨称之为紧急偏移。TCP的紧急指针是发送端向接收端发送紧急数据的方法。
|
||
|
||
TCP头部选项:TCP头部的最后一个选项字段(options)是可变长的可选信息。这部分最多包含40字节,因为TCP头部最长是60字节(其中还包含前面讨论的20字节的固定部分)。典型的TCP头部选项结构如下图所示。
|
||
|
||
![[Pasted image 20240415134347.png]]
|
||
|
||
选项的第一个字段kind说明选项的类型。有的TCP选项没有后面两个字段,仅包含1字节的kind字段。第二个字段length(如果有的的话)指定该选项的总长度,该长度包括kind字段和length字段占据的2字节。第三个字段info(如果有的话)是选项的具体信息。常见的TCP选项有7中,如下图
|
||
|
||
![[Pasted image 20240415134357.png]]
|
||
|
||
kind=0是选项表结束选项。
|
||
|
||
kind=1是空操作(nop)选项,没有特殊含义,一般用于将TCP选项的总长度填充为4字节的整数倍。
|
||
|
||
kind=2是最大报文段长度选项。TCP连接初始化时,通信双方使用该选项来协商最大报文段长度(Max Segement Size,MSS)。TCP模块通常将MSS设置为(MTU-40)字节(减掉的这40字节包括20字节的TCP头部和20字节的IP头部)。这样携带TCP报文段的IP数据报的长度就不会超过MTU(假设TCP头部和IP头部都不包含选项字段,并且这也是一般情况),从而避免本机发生IP分片。对以太网而言,MSS值是1460(1500-40)字节。
|
||
|
||
kind=3是窗口扩大因子选项。TCP连接初始化时,通信双方使用该选项来协商接收通告窗口的扩大因子。在TCP的头部中,接收通告窗口大小时用16位表示的,故最大为65535字节,但实际上TCP模块允许的接收通告窗口大小远不止这个数(为了提高TCP通信的吞吐量)。窗口扩大因子解决了这个问题。假设TCP头部中的接收通告窗口大小是N乘2的M次方,或者说N左移M位。注意,M的取值范围是0~14。我们可以通过修改/proc/sys/net/ipv4/tcp_window_scaling内核变量来启用或关闭窗口扩大因子选项。
|
||
|
||
kind=5是SACK实际工作的选项。该选项的参数告诉发送方本端已经收到并缓存的不连续的数据块,从而让发送端可以据此检查并重发丢失的数据块。每个块边沿(edge of block)参数包含一个4字节的序号。其中块左边沿表示不连续块的第一个数据的序号,而块右边沿则表示不连续块的最后一个数据的序号的下一个序号。这样一对参数(块左边沿和块右边沿)之间的数据是没有收到的。因为一个块信息占用8字节,所以TCP头部选项中实际上最多可以包含4个这样的不连续数据块(考虑选项类型和长度占用的2字节)。
|
||
|
||
kind=8是时间戳选项。该选项提供了较为准确的计算通信双方之间的回路时间(Round Trip Time,RTT)的方法,从而为TCP流量控制提供重要信息。我们可以通过修改/proc/sys/net/ipv4/tcp_timestamps内核变量来启用或关闭时间戳选项。
|
||
|
||
以下是抓到的一个TCP报文段:
|
||
|
||
![[Pasted image 20240415134412.png]]
|
||
|
||
从图中可以看出源端口号58991对应e66f,目的端口号443,对应01bb,Flags为SYN,表示请求一个连接,因此它是一个同步报文段。这里seq表示的是相对序号,因为该同步报文段是从180.85.25.164到74.125.204.100的第一个TCP报文段,所以该序号值也就是此次通信过程中该传输方向的ISN值,则相对序号即为0。并且,因为这是通信过程中第一个TCP报文段,所以它没有针对对方发送来的TCP报文段的确认值(尚未收到任何对方发来的TCP报文段)。
|
||
|
||
window size是接收通告窗口大小,因为这是一个同步报文段,所以win值反映的是实际的接收通告窗口大小。 |