TCP协议

一、TCP协议介绍

TCP(Transmission Control Protocol, 传输控制协议/网际协议),也叫网络通讯协议,是指能够在多个不同网络间实现信息传输的协议簇。

TCP/IP协议不仅仅指的是TCP 和IP两个协议,而是指一个由FTP、SMTP、TCP、UDP、IP等协议构成的协议簇,只是因为在TCP/IP协议中TCP协议和IP协议最具代表性,所以被称为TCP/IP协议

网络传输控制协议是一种面向连接的、可靠的、基于字节流的传输层通信协议


特点:
面向连接(可靠传输)
可靠按序交付
不支持多播和广播,开销大
基于字节流
传输的数据单位是TCP报文段

 

 二、TCP报文字段

报文段示意图如下

 

源端口号
占16bit,包含初始化通信的端口,源端口和源IP地址的作用是标识报文的返回地址


目的端口号
占16bit,目的端口域定义传输的目的,指明报文接收计算机上的应用程序地址接口


序列号(Sequence Number, seq Number)
占32bit,表示本报文段所发送数据的第一个字节的编号,在TCP连接中,所传送的字节流的每一个字节都会按顺序编号


确认号(Acknowledgment Number, ack Number)
占32bit, 表示接收方期望收到发送方下一个报文段的第一个字节数据的编号


数据偏移字段
占4bit,TCP 首部长度(Header Length), 指数据段中的数据部分起始处距离 TCP 数据段起始处的字节偏移量,包括TCP头大小,告诉接收端的应用程序,数据从何处开始

 

保留字段(Reserved)
占4bit,如果有新的标志位字段,会占用一位,目前必须全部为0,为了将来定义新的用途而保留

 

标志位字段,占8bit
  CWR(Congestion Window Reduced):拥塞窗口减少标志,用来表明它接收到了设置 ECE 标志的 TCP 包。并且,发送方收到消息之后,通过减小发送窗口的大小来降低发送速率
  ECE(ECN-Echo):在 TCP 三次握手时表明一个 TCP 端是具备 ECN (显式拥塞通告)功能的。在数据传输过程中,它也用来表明接收到的 TCP 包的 IP 头部的 ECN 被设置为 11,即网络线路拥堵
  URG(urgent):紧急标志,表示本报文段中发送的数据是否包含紧急数据。 当URG=1表示表示有紧急数据, 紧急指针字段才有效
  ACK(acknowledgement):确认标志,确认是否收到了请求。TCP规定:连接建立后,ACK必须为1
  PSH(push):推标志。PSH=1表示接收端不将该数据进行队列处理,而是尽可能快地将数据转由应用处理
  RST(reset):重置连接标志,用于复位相应的TCP连接。 当RST=1表示TCP连接出现了严重错误(如主机崩溃),必须释放连接,然后再重新建立连接
  SYN(synchronous):请求建立连接标志,仅在第一次握手请求建立TCP连接时有效。当 SYN=1,ACK=0时, 表示这是一个请求建立连接的报文段;当 SYN=1,ACK=1 时, 表示同意建立连接
  FIN(finish):断开连接标志。FIN=1表示请求断开连接

 

窗口大小(Window Size)
占16bit,用来表示想收到的每个TCP数据段的大小, 即从 ack Number 开始还可以接收多少字节的数据量,也表示当前接收端的接收窗口还有多少剩余空间,该字段可以用于 TCP 的流量控制。
TCP的流量控制由该字段值大小来控制,窗口大小最大为65535Byte

 

校验和(TCP CheckSum)
占16bit,用于确认传输的数据是否有损坏。
发送端基于数据内容校验生成一个数值,接收端根据接收的数据校验生成一个值, 两个值必须相同,才能证明数据是有效的, 如果两个值不同,则丢掉这个数据包
检验和覆盖了整个的TCP报文段: 这是一个强制性的字段,一定是由发送端计算和存储,并由接收端进行验证的
根据伪头 + TCP 头 + TCP 数据三部分进行计算的

 

紧急指针(Urgent Pointer)
占16bit, 表示本数据段中为紧急数据的字节数。当URG=1时才有效;否则,紧急域作为填充,加快处理标示为紧急的数据段

 

选项(Option)
长度不定,但长度必须为32bits 的整数倍。如果没有选项就表示这个1字节的域等于0

 

数据
该TCP协议包负载的数据

 

 

三、三次握手

三次握手过程流程图如下

 

 

 

 

1 握手过程

第一次握手(Client发送SYN报文)
Client发送一个SYN=1,seq=x(随机产生)的请求连接报文, Client进入SYN_SENT状态
TCP 协议规定,SYN=1 的报文段不能携带数据,但是要消耗一个序号,第一次握手只有序列号没有确认号


第二次握手(Server发送SYN-ACK报文)
Server收到Client的SYN报文后知道Client请求建立连接,Server发送一个SYN=1, ACK=1,ack=x+1,seq=y(随机产生)报文,Server进入SYN_RCVD状态

 

第三次握手(Client发送ACK报文)
Client收到SYN-ACK报文,检查报文的ack是否为x+1,ACK是否为1,如果正确则发送一个ACK=1,ack=y+1,seq=x+1的报文
Server检查ACK报文的ack是否为y+1,ACK是否为1,如果正确则连接建立成功,Client和Server进入ESTABLISHED状态,完成三次握手,随后Client与Server之间可以开始传输数据

 

2 握手过程的状态和参数

1) SYN_SENT
表示Client已发送SYN报文
Client发送SYN报文,随即进入SYN_SENT状态,等待接收Server发送的SYN-ACK报文
 
2) SYN_RCVD
表示Server已经接受到SYN报文并发送了SYN-ACK报文,在正常情况下,这个状态TCP三次握手过程中一个很短暂的中间状态
处于SYN-RCVD状态的Server在收到Client的ACK报文后,进入ESTABLISHED状态
 
3) ESTABLISHED
表示连接已经建立
 
4) 半连接队列,全连接队列
TCP三次握手过程中,维护两个队列:
半连接队列:syn队列
全连接队列:accept队列,全连接队列过小时,容易发生全连接队列溢出,后续建立连接的请求就会被丢弃
全连接队列的最大值取决于 somaxconn 和 backlog 之间的最小值,即 min(somaxconn, backlog),增大全连接队列需要提高这两个参数的大小

Server收到SYN报文后,把该连接存储到半连接队列,并向发送SYN-ACK报文

Server收到ACK报文后,把连接从半连接队列移除,然后创建新的完全连接,并将其添加到全连接队列,等待进程调用accept函数时把连接取出来

5) 半连接存活时间
是指半连接队列中请求存活的最长时间,即Server从收到SYN包到确认这个报文无效的最长时间,该时间值是所有重传请求包的最长等待时间总和
也称半连接存活时间为Timeout时间、SYN_RCVD存活时间
 

 

 

 

四、四次挥手

四次挥手过程流程图如下

 

 

TCP连接是全双工的,因此每个方向都必须要单独进行关闭

当一方完成数据发送任务后,发送一个FIN报文来终止这一方向的连接,收到FIN报文只是意味着这一方向上不会再收到数据了,但是在这个TCP连接上仍然能够发送数据,直到另一方向也发送了FIN

首先进行关闭的一方将执行主动关闭,另一方则执行被动关闭

 

1 挥手过程

第一次挥手(Client发送FIN报文)
Client发送一个FIN=1,seq=x(已经传送的数据的最后一个字节的序号加1),ack=y的报文,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态


第二次挥手(Server发送ACK报文)
Server收到Client的FIN报文后,发送一个ACK=1,seq=y,ack=x+1的报文,Server进入CLOSE_WAIT状态


第三次挥手(Server发送FIN报文)

Client收到Server的 ACK 应答报文后,Client进入 FIN_WAIT_2 状态
Server处理完数据之后,发送一个FIN=1,seq=z(已经传送的数据的最后一个字节的序号加1),ack=x+1的报文,用来关闭Server到Client的数据传送,进入LAST_ACK状态


第四次挥手(Client发送ACK报文)
Client收到Server的FIN报文后,发送一个ACK=1,seq=x+1,ack=z+1的报文,Client进入TIME_WAIT状态

Server收到了Client的ACK报文后,进入CLOSED状态

此时Client并未进入CLOSED状态,为了防止服务延迟,需要等待2MSL的时间,然后进入CLOSED状态,如果网络异常,Client可以重发ACK应答报文

 

2 挥手过程的状态和参数

1) FIN_WAIT_1, FIN_WAIT_2

FIN_WAIT_1:表示主动关闭方已经发送了FIN报文,FIN_WAIT_1状态一般是比较难见到

FIN_WAIT_2:表示主动关闭方已经收到对方的ACK报文,即半关闭状态。此时被动关闭方可能暂时还有数据需要传送,需要主动关闭方等被动关闭方的数据传送完

FIN_WAIT_2状态不可以持续太久,Linux 系统中FIN_WAIT_2状态持续时间由 tcp_fin_timeout 参数决定,参数位于 /proc/sys/net/ipv4/ tcp_fin_timeout  文件,默认值 60 秒

如果在 tcp_fin_timeout 时间后还没有收到对方的FIN 报文,主动关闭方的连接就会直接关闭

 

2) CLOSE_WAIT

表示被动关闭方已经收到FIN报文,且也发送了ACK报文

被动关闭方可能还有数据发送给主动关闭方,如果没有的话,那么也发送FIN报文给对方;如果有,则处理完数据之后在发送FIN报文给主动关闭方

 

3) LAST_ACK

表示被动关闭方已经发送了FIN报文

 

4) TIME_WAIT

表示主动关闭方已经发送ACK报文,

处于IME_WAIT状态下的主动关闭方需要等2MSL后才能进入CLOSED状态

如果FIN_WAIT_1状态下,收到了对方同时带FIN标志和ACK标志的报文时,可以直接进入到TIME_WAIT状态,而无须经过FIN_WAIT_2状态

 

5) CLOSED

表示初始状态

 

 

五、TCP握手、挥手过程产生的问题

TCP握手问题

1) TCP第一次握手的SYN报文丢失
Client会超时重传 SYN 报文,且重传的 SYN 报文的序列号都是一样的,超过最大重传次数后断开连接

不同版本的操作系统超时时间可能不同,有1s,3s等,超时时间固定在系统的内核中,想要更改则需要重新编译内核

Linux系统中Client的 SYN 报文最大重传次数由tcp_syn-retries参数决定,参数位于 /proc/sys/net/ipv4/tcp_syn_retries 文件,可以自定义,默认值5

每重传一次,超时时间是上一次重传的超时时间*2

 

2) TCP第二次握手的SYN-ACK报文丢失
Client发送的SYN报文没有收到ACK,会超时重传SYN报文,超过最大重传次数后断开连接
Server发送的SYN-ACK报文没有收到ACK,会超时重传重传SYN-ACK报文,超过最大重传次数后断开连接

Linux系统中SYN-ACK报文的最大重传次数由tcp_synack-retries参数决定,参数位于 /proc/sys/net/ipv4/tcp_synack_retries 文件,默认值5

每重传一次,超时时间是上一次重传的超时时间*2

 

3) TCP第三次握手的 ACK 报文丢失
Server发送的SYN-ACK报文没有收到ACK,超时重传重传SYN-ACK报文,超过最大重传次数后断开连接
每重传一次,超时时间是上一次重传的超时时间*2

 

4) 初始序列号为什么随机产生

为了网络安全,如果不随机产生初始序列号,黑客将很容易获取到当前主机与其他主机之间通信的初始化序列号,并且伪造序列号进行攻击

 

5) 第一次握手Client发送的SYN包不携带数据却要消耗一个seq
Client发送的SYN报文需要Sever的确认,需要占用一个seq确保Server确认不会出现歧义:如果不占seq,Server的确认是对传输数据的确认还是对SYN报文的确认

 

 6 TCP为什么需要三次握手?而不是两次?或者更多次?

两次握手:建立重复连接导致连接混乱
多次握手:浪费资源
Client发送SYN报文,由于某种原因长时间无法到达Server,Client没有收到Server的SYN-ACK报文,会重传SYN报文,若此时,前面的SYN报文(已经失效)到达了Server,Server收到该FIN报文后误以为是Client发出新的请求连接,于是发出SYN-ACK报文同意建立连接:

如果是二次握手:导致重复连接

如果是三次握手:Client收到SYN-ACK后校验未通过,Server没有收到Client的确认应答消息,不建立连接

 

2 TCP挥手问题

1) TCP第一次挥手FIN报文丢失

Client发送的FIN报文没有收到ACK,会超时重传FIN报文,超过最大重传次数后断开连接

Linux系统中FIN报文的最大重传次数由tcp_orphan_retries参数决定,参数位于 /proc/sys/net/ipv4/tcp_orphan_retries 文件,默认值0

每重传一次,超时时间是上一次重传的超时时间*2

 

2) TCP第二次挥手ACK报文丢失

Client发送的FIN报文没有收到ACK,会超时重传FIN报文,超过最大重传次数后断开连接

每重传一次,超时时间是上一次重传的超时时间*2

 

3) TCP第三次挥手FIN报文丢失

Server发送的FIN报文没有收到ACK,会超时重传FIN报文,超过最大重传次数后断开连接

每重传一次,超时时间是上一次重传的超时时间*2

 

4) TCP第四次挥手手ACK报文丢失

Server发送的FIN报文没有收到ACK,会超时重传FIN报文,超过最大重传次数后断开连接

每重传一次,超时时间是上一次重传的超时时间*2

 

5) 为什么不是三次挥手?
Client要关闭连接时,无法保证Server已经将数据发送完成,Server收到Client关闭连接请求后,只能发送一个ACK报文

Server需要把数据发送完成之后,然后发送一个FIN后,Client收到FIN报文后也要发送一个ACK报文,同意关闭连接

如果Client不发送ACK报文,Server认为发送的FIN报文丢了,触发超时重传

 

6) 为什么Client在TIME_WAIT状态需要经过2MSL才能进入CLOSED状态

如果Client直接进入CLOSED状态,由于TCP协议的不可靠性或者其它网络原因,导致Server没有收到Client的ACK报文,Server会超时重传

处于TIME_WAIT状态下的Client不能立即关闭,它必须保证Server接收到了ACK报文,最后正确的关闭连接
如果Client在2MSL时间内再次收到Server的FIN报文,那么Client重发ACK报文并再次等待2MSL,如果直到2MSL,Client都没有再次收到FIN报文,那么Client推断ACK应答报文已经被成功接收,则结束TCP连接

MSL:Maximum Segment Lifetime,即最大报文段生存时间,指一个片段在网络中最大的存活时间,MSL就是一个发送和一个回复所需的最大时间,典型的值为30秒、1分钟和2分钟

 

 

3 如何抵御DDos攻击

Client不停的发SYN报文,但不发送ACK报文,此时Server有大量请求处于syn_rcvd状态,即所谓的syn flood泛洪,syn攻击,DDos攻击

 

1) 增大半连接队列
不能只增大tcp_max_syn_backlog,还需要增大somaconn和backlog,即增大全连接队列

 

2) 开启tcp_syncookies功能
开启tcp_syncookies就可以在不使用syn半连接队列的情况下建立连接
在接收到Client的SYN报文时,计算出一个值,放到SYN-ACK报文中。当Client发送ACK报文时,取出该值验证,成功则建立连接


3) 减少ACK-SYN报文的重传次数
Server发送SYN-ACK报文接收不到Client的ACK报文,Server会重传SYN-ACK报文,超过最大重传次数后断开连接

针对syn攻击的场景,可以减少SYN-ACK报文的最大重传次数,使处于syn_rcvd状态的请求更快断开连接

 

 

4 为什么挥手要比握手多一次

TCP三次握手时Server可以把ACK和SYN放在同一个报文(SYN-ACK)里来发送
TCP四次挥手时,当收到对方的FIN报文时,仅仅表示对方已经没有数据发送,但是己方可能还有数据发送给对方,等己方把数据给发送完之后,再发送 FIN 报文给对方表示请求关闭连接,所以 ACK 报文和 FIN 报文都是分开发送

 

 

5 如果已经建立了连接,client突然出现故障

TCP设有一个保活计时器(时间通常是设置为2小时,Server每收到一次Client的请求后都会重新复位),Server在保活计时器时间内没有收到Client的任何数据,Server就会发送一个探测报文段,以后每隔75秒钟发送一次,若连续发送10个探测报文仍然没反应,Server就认为Client出了故障,接着就关闭连接

 

posted @ 2022-12-18 17:03  junffzhou  阅读(129)  评论(0编辑  收藏  举报