由于网络处理的流程最复杂,跟我们前面讲到的进程调度、中断处理、内存管理以及 I/O 等都密不可分,所以,我把网络模块作为最后一个资源模块来讲解。
同 CPU、内存以及 I/O 一样,网络也是 Linux 系统最核心的功能。网络是一种把不同计算机或网络设备连接到一起的技术,它本质上是一种进程间通信方式,特别是跨系统的进程间通信,必须要通过网络才能进行。随着高并发、分布式、云计算、微服务等技术的普及,网络的性能也变得越来越重要。
说到网络,我想你肯定经常提起七层负载均衡、四层负载均衡,或者三层设备、二层设备等等。那么,这里说的二层、三层、四层、七层又都是什么意思呢?
实际上,这些层都来自国际标准化组织制定的开放式系统互联通信参考模型(Open System Interconnection Reference Model),简称为 OSI 网络模型。
但是 OSI 模型还是太复杂了,也没能提供一个可实现的方法。所以,在 Linux 中,我们实际上使用的是另一个更实用的四层模型,即 TCP/IP 网络模型。
TCP/IP 模型,把网络互联的框架分为应用层、传输层、网络层、网络接口层等四层,其中,
为了帮你更形象理解 TCP/IP 与 OSI 模型的关系,我画了一张图,如下所示:
当然了,虽说 Linux 实际按照 TCP/IP 模型,实现了网络协议栈,但在平时的学习交流中,我们习惯上还是用 OSI 七层模型来描述。比如,说到七层和四层负载均衡,对应的分别是 OSI 模型中的应用层和传输层(而它们对应到 TCP/IP 模型中,实际上是四层和三层)。
OSI引入了服务、接口、协议、分层的概念,TCP/IP借鉴了OSI的这些概念建立TCP/IP模型。
OSI先有模型,后有协议,先有标准,后进行实践;而TCP/IP则相反,先有协议和应用再提出了模型,且是参照的OSI模型。
OSI是一种理论下的模型,而TCP/IP已被广泛使用,成为网络互联事实上的标准。
有了 TCP/IP 模型后,在进行网络传输时,数据包就会按照协议栈,对上一层发来的数据进行逐层处理;然后封装上该层的协议头,再发送给下一层。
当然,网络包在每一层的处理逻辑,都取决于各层采用的网络协议。比如在应用层,一个提供 REST API 的应用,可以使用 HTTP 协议,把它需要传输的 JSON 数据封装到 HTTP 协议中,然后向下传递给 TCP 层。
而封装做的事情就很简单了,只是在原来的负载前后,增加固定格式的元数据,原始的负载数据并不会被修改。
比如,以通过 TCP 协议通信的网络包为例,通过下面这张图,我们可以看到,应用程序数据在每个层的封装格式。
这些新增的头部和尾部,增加了网络包的大小,但我们都知道,物理链路中并不能传输任意大小的数据包。网络接口配置的最大传输单元(MTU),就规定了最大的 IP 包大小。在我们最常用的以太网中,MTU 默认值是 1500(这也是 Linux 的默认值)。
一旦网络包超过 MTU 的大小,就会在网络层分片,以保证分片后的 IP 包不大于 MTU 值。显然,MTU 越大,需要的分包也就越少,自然,网络吞吐能力就越好。
理解了 TCP/IP 网络模型和网络包的封装原理后,你很容易能想到,Linux 内核中的网络栈,其实也类似于 TCP/IP 的四层结构。如下图所示,就是 Linux 通用 IP 网络栈的示意图:
我们从上到下来看这个网络栈,你可以发现,
这里我简单说一下网卡。网卡是发送和接收网络包的基本设备。在系统启动过程中,网卡通过内核中的网卡驱动程序注册到系统中。而在网络收发过程中,内核通过中断跟网卡进行交互。
再结合前面提到的 Linux 网络栈,可以看出,网络包的处理非常复杂。所以,网卡硬中断只处理最核心的网卡数据读取或发送,而协议栈中的大部分逻辑,都会放到软中断中处理。
我们先来看网络包的接收流程。
当一个网络帧到达网卡后,网卡会通过 DMA 方式,把这个网络包放到收包队列中;然后通过硬中断,告诉中断处理程序已经收到了网络包。
接着,网卡中断处理程序会为网络帧分配内核数据结构(sk_buff),并将其拷贝到 sk_buff 缓冲区中;然后再通过软中断,通知内核收到了新的网络帧。
接下来,内核协议栈从缓冲区中取出网络帧,并通过网络协议栈,从下到上逐层处理这个网络帧。比如,
最后,应用程序就可以使用 Socket 接口,读取到新接收到的数据了。
为了更清晰表示这个流程,我画了一张图,这张图的左半部分表示接收流程,而图中的粉色箭头则表示网络包的处理路径。
了解网络包的接收流程后,就很容易理解网络包的发送流程。网络包的发送流程就是上图的右半部分,很容易发现,网络包的发送方向,正好跟接收方向相反。
首先,应用程序调用 Socket API(比如 sendmsg)发送网络包。
由于这是一个系统调用,所以会陷入到内核态的套接字层中。套接字层会把数据包放到 Socket 发送缓冲区中。
接下来,网络协议栈从 Socket 发送缓冲区中,取出数据包;再按照 TCP/IP 栈,从上到下逐层处理。比如,传输层和网络层,分别为其增加 TCP 头和 IP 头,执行路由查找确认下一跳的 IP,并按照 MTU 大小进行分片。
分片后的网络包,再送到网络接口层,进行物理地址寻址,以找到下一跳的 MAC 地址。然后添加帧头和帧尾,放到发包队列中。这一切完成后,会有软中断通知驱动程序:发包队列中有新的网络帧需要发送。
最后,驱动程序通过 DMA ,从发包队列中读出网络帧,并通过物理网卡把它发送出去。
多台服务器通过网卡、交换机、路由器等网络设备连接到一起,构成了相互连接的网络。由于网络设备的异构性和网络协议的复杂性,国际标准化组织定义了一个七层的 OSI 网络模型,但是这个模型过于复杂,实际工作中的事实标准,是更为实用的 TCP/IP 模型。
TCP/IP 模型,把网络互联的框架,分为应用层、传输层、网络层、网络接口层等四层,这也是 Linux 网络栈最核心的构成部分。
我结合网络上查阅的资料和文章中的内容,总结了下网卡收发报文的过程,不知道是否正确:
当发送数据包时,与上述相反。链路层将数据包封装完毕后,放入网卡的DMA缓冲区,并调用系统硬中断,通知网卡从缓冲区读取并发送数据。
了解 Linux 网络的基本原理和收发流程后,你肯定迫不及待想知道,如何去观察网络的性能情况。具体而言,哪些指标可以用来衡量 Linux 的网络性能呢?
实际上,我们通常用带宽、吞吐量、延时、PPS(Packet Per Second)等指标衡量网络的性能。
除了这些指标,网络的可用性(网络能否正常通信)、并发连接数(TCP 连接数量)、丢包率(丢包百分比)、重传率(重新传输的网络包比例)等也是常用的性能指标。
分析网络问题的第一步,通常是查看网络接口的配置和状态。你可以使用 ifconfig 或者 ip 命令,来查看网络的配置。我个人更推荐使用 ip 工具,因为它提供了更丰富的功能和更易用的接口。
以网络接口 eth0 为例,你可以运行下面的两个命令,查看它的配置和状态:
你可以看到,ifconfig 和 ip 命令输出的指标基本相同,只是显示格式略微不同。比如,它们都包括了网络接口的状态标志、MTU 大小、IP、子网、MAC 地址以及网络包收发的统计信息。
第一,网络接口的状态标志。ifconfig 输出中的 RUNNING ,或 ip 输出中的 LOWER_UP ,都表示物理网络是连通的,即网卡已经连接到了交换机或者路由器中。如果你看不到它们,通常表示网线被拔掉了。
第二,MTU 的大小。MTU 默认大小是 1500,根据网络架构的不同(比如是否使用了 VXLAN 等叠加网络),你可能需要调大或者调小 MTU 的数值。
第三,网络接口的 IP 地址、子网以及 MAC 地址。这些都是保障网络功能正常工作所必需的,你需要确保配置正确。
第四,网络收发的字节数、包数、错误数以及丢包情况,特别是 TX 和 RX 部分的 errors、dropped、overruns、carrier 以及 collisions 等指标不为 0 时,通常表示出现了网络 I/O 问题。其中:
ifconfig 和 ip 只显示了网络接口收发数据包的统计信息,但在实际的性能问题中,网络协议栈中的统计信息,我们也必须关注。你可以用 netstat 或者 ss ,来查看套接字、网络栈、网络接口以及路由表的信息。
我个人更推荐,使用 ss 来查询网络的连接信息,因为它比 netstat 提供了更好的性能(速度更快)。
比如,你可以执行下面的命令,查询套接字信息:
netstat 和 ss 的输出也是类似的,都展示了套接字的状态、接收队列、发送队列、本地地址、远端地址、进程 PID 和进程名称等。
其中,接收队列(Recv-Q)和发送队列(Send-Q)需要你特别关注,它们通常应该是 0。当你发现它们不是 0 时,说明有网络包的堆积发生。当然还要注意,在不同套接字状态下,它们的含义不同。
当套接字处于连接状态(Established)时,
当套接字处于监听状态(Listening)时,
所谓全连接,是指服务器收到了客户端的 ACK,完成了 TCP 三次握手,然后就会把这个连接挪到全连接队列中。这些全连接中的套接字,还需要被 accept() 系统调用取走,服务器才可以开始真正处理客户端的请求。
与全连接队列相对应的,还有一个半连接队列。所谓半连接是指还没有完成 TCP 三次握手的连接,连接只进行了一半。服务器收到了客户端的 SYN 包后,就会把这个连接放到半连接队列中,然后再向客户端发送 SYN+ACK 包。
类似的,使用 netstat 或 ss ,也可以查看协议栈的信息:
这些协议栈的统计信息都很直观。ss 只显示已经连接、关闭、孤儿套接字等简要统计,而 netstat 则提供的是更详细的网络协议栈信息。
比如,上面 netstat 的输出示例,就展示了 TCP 协议的主动连接、被动连接、失败重试、发送和接收的分段数量等各种信息。
接下来,我们再来看看,如何查看系统当前的网络吞吐量和 PPS。在这里,我推荐使用我们的老朋友 sar,在前面的 CPU、内存和 I/O 模块中,我们已经多次用到它。
给 sar 增加 -n 参数就可以查看网络的统计信息,比如网络接口(DEV)、网络接口错误(EDEV)、TCP、UDP、ICMP 等等。执行下面的命令,你就可以得到网络接口统计信息:
这儿输出的指标比较多,我来简单解释下它们的含义。
其中,Bandwidth 可以用 ethtool 来查询,它的单位通常是 Gb/s 或者 Mb/s,不过注意这里小写字母 b ,表示比特而不是字节。我们通常提到的千兆网卡、万兆网卡等,单位也都是比特。如下你可以看到,我的 eth0 网卡就是一个千兆网卡:
其中,Bandwidth 可以用 ethtool 来查询,它的单位通常是 Gb/s 或者 Mb/s,不过注意这里小写字母 b ,表示比特而不是字节。我们通常提到的千兆网卡、万兆网卡等,单位也都是比特。如下你可以看到,我的 eth0 网卡就是一个千兆网卡:
我们通常使用带宽、吞吐量、延时等指标,来衡量网络的性能;相应的,你可以用 ifconfig、netstat、ss、sar、ping 等工具,来查看这些网络的性能指标。
小狗同学问到: 老师,您好 ss —lntp 这个 当session处于listening中 rec-q 确定是 syn的backlog吗?
A: Recv-Q为全连接队列当前使用了多少。 中文资料里这个问题讲得最明白的文章: https://mp.weixin.qq.com/s/yH3PzGEFopbpA-jw4MythQ
看了源码发现,这个地方讲的有问题.关于ss输出中listen状态套接字的Recv-Q表示全连接队列当前使用了多少,也就是全连接队列的当前长度,而Send-Q表示全连接队列的最大长度
tcp 协议 是互联网中最常用的协议 , 开发人员基本上天天和它打交道,对它进行深入了解。 可以帮助我们排查定位bug和进行程序优化。下面我将就TCP几个点做深入的探讨
客户端:收到 ack 后 分配连接资源。 发送数据
服务器 : 收到 syn 后立即 分配连接资源
客户端:收到ACK, 立即分配资源
服务器:收到ACK, 立即分配资源
既然三次握手也不是100%可靠, 那四次,五次,六次。。。呢? 其实都一样,不管多少次都有丢包问题。
client 只发送一个 SYN, server 分配一个tcb, 放入syn队列中。 这时候连接叫 半连接 状态;如果server 收不到 client 的ACK, 会不停重试 发送 ACK-SYN 给client 。重试间隔 为 2 的 N 次方 叠加(2^0 , 2^1, 2^2 ....);直至超时才释放syn队列中的这个 TCB
在半连接状态下, 一方面会占用队列配额资源,另一方面占用内存资源。我们应该让半连接状态存在时间尽可能的小
当client 向一个未打开的端口发起连接请求时,会收到一个RST回复包
当listen 的 backlog 和 somaxconn 都设置了得时候, 取两者min值
Recv-Q 是accept 队列当前个数, Send-Q 设置最大值
这种SYN洪水攻击是一种常见攻击方式,就是利用半连接队列特性,占满syn 队列的 资源,导致 client无法连接上。
解决方案:
为什么不像握手那样合并成三次挥手? 因为和刚开始连接情况,连接是大家都从0开始, 关闭时有历史包袱的。server(被动关闭方) 收到 client(主动关闭方) 的关闭请求FIN包。 这时候可能还有未发送完的数据,不能丢弃。 所以需要分开。事实可能是这样
当然,在没有待发数据,并且允许 Delay ACK 情况下, FIN-ACK合并还是非常常见的事情,这是三次挥手是可以的。
同上
CLOSE_WAIT 是被动关闭方才有的状态 。
被动关闭方 [收到 FIN 包 发送 ACK 应答] 到 [发送FIN, 收到ACK ] 期间的状态为 CLOSE_WAIT, 这个状态仍然能发送数据。 我们叫做 半关闭 , 下面用个例子来分析:
这个是我实际生产环境碰到的一个问题,长连接会话场景,server端收到client的rpc call 请求1,处理发现请求包有问题,就强制关闭结束这次会话, 但是 因为client 发送 第二次请求之前,并没有去调用recv,所以并不知道 这个连接被server关闭, 继续发送 请求2 , 此时是半连接,能够成功发送到对端机器,但是recv结果后,遇到连接已经关闭错误。
如果 client 和 server 恰好同时发起关闭连接。这种情况下,两边都是主动连接,都会进入 TIME_WAIT状态
1、 被动关闭方在LAST_ACK状态(已经发送FIN),等待主动关闭方的ACK应答,但是 ACK丢掉, 主动方并不知道,以为成功关闭。因为没有TIME_WAIT等待时间,可以立即创建新的连接, 新的连接发送SYN到前面那个未关闭的被动方,被动方认为是收到错误指令,会发送RST。导致创建连接失败。
2、 主动关闭方断开连接,如果没有TIME_WAIT等待时间,可以马上建立一个新的连接,但是前一个已经断开连接的,延迟到达的数据包。 被新建的连接接收,如果刚好seq 和 ack字段 都正确, seq在滑动窗口范围内(只能说机率非常小,但是还是有可能会发生),会被当成正确数据包接收,导致数据串包。 如果不在window范围内,则没有影响( 发送一个确认报文(ack 字段为期望ack的序列号,seq为当前发送序列号),状态变保持原样)
TIME_WAIT 问题比较比较常见,特别是CGI机器,并发量高,大量连接后段服务的tcp短连接。因此也衍生出了多种手段解决。虽然每种方法解决不是那么完美,但是带来的好处一般多于坏处。还是在日常工作中会使用。
1、改短TIME_WAIT 等待时间
这个是第一个想到的解决办法,既然等待时间太长,就改成时间短,快速回收端口。但是实际情况往往不乐观,对于并发的机器,你改多短才能保证回收速度呢,有时候几秒钟就几万个连接。太短的话,就会有前面两种问题小概率发生。
2、禁止Socket lingering
这种情况下关闭连接,会直接抛弃缓冲区中待发送的数据,会发送一个RST给对端,相当于直接抛弃TIME_WAIT, 进入CLOSE状态。同样因为取消了 TIME_WAIT 状态,会有前面两种问题小概率发生。
3、tcp_tw_reuse
net.ipv4.tcp_tw_reuse选项是 从 TIME_WAIT 状态的队列中,选取条件:1、remote 的 ip 和端口相同, 2、选取一个时间戳小于当前时间戳; 用来解决端口不足的尴尬。
现在端口可以复用了,看看如何面对前面TIME_WAIT 那两种问题。 我们仔细回顾用一下前面两种问题。 都是在新建连接中收到老连接的包导致的问题 , 那么如果我能在新连接中识别出此包为非法包,是不是就可以丢掉这些无用包,解决问题呢。
需要实现这些功能,需要扩展一下tcp 包头。 增加 时间戳字段。 发送者 在每次发送的时候。 在tcp包头里面带上发送时候的时间戳。 当接收者接收的时候,在ACK应答中除了TCP包头中带自己此时发送的时间戳,并且把收到的时间戳附加在后面。也就是说ACK包中有两个时间戳字段。结构如下:
那我们接下来一个个分析tcp_tw_reuse是如何解决TIME_WAIT的两个问题的
4、tcp_tw_recycle
tcp_tw_recycle 也是借助 timestamp机制。顾名思义, tcp_tw_reuse 是复用 端口,并不会减少 TIME-WAIT 数量。你去查询机器上TIME-WAIT 数量,还是 几千几万个,这点对有强迫症的同学感觉很不舒服。tcp_tw_recycle 是 提前 回收 TIME-WAIT资源。会减少 机器上 TIME-WAIT 数量。
tcp_tw_recycle 工作原理是。
1、先提出一个问题, 可以不进行三次握手直接往服务端发送数据包吗?
是不可以的,也是可以的 ;
1)不可以是因为现在的TCP连接标准和规范要求传输数据前先确认两端的状态,有一端状态不OK的话,发数据包有什么用呢;
2)说可以是站在网络连接的角度,像 UDP 协议;
2、TCP三次握手
1)标志位、随机序列号和确认序列号是在数据包的 TCP 首部里面;
2)几个状态是指客户端和服务端连接过程中 socket 状态;
3)第一次握手,客户端向服务端发送数据包,该数据包中 SYN 标志位为 1,还有随机生成的序列号c_seq,客户端状态改为 SYN-SENT ;
4)第二次握手,服务端接收到客户端发过来的数据包中 SYN 标志位为 1,就知道客户端想和自己建立连接,服务端会根据自身的情况决定是拒绝连接,或确定连接,还是丢弃该数据包;
拒绝连接,会往客户端发一个数据包,该数据包中 RST 标志位为 1,客户端会报 Connection refused ;
丢弃客户端的数据包,超过一定时间后客户端会报 Connection timeout;
确定连接时会往客户端发一个数据包,该数据包中 ACK 标志位为 1,确认序列号 ack=c_seq+1,SYN 标志位为 1,随机序列号 s_seq,状态由 LISTEN 改为 SYN-RCVD ;
5)第三次握手,客户端接收到数据包会做校验,校验ACK标志位和确认序列号 ack=c_seq+1,如果确定是服务端的确认数据包,改自己的状态为 ESTABLISHED ,并给服务端发确认数据包;
6)服务端接到客户端数据包,会校验ACK标志位和确认序列号 ack=s_seq+1,改自己的状态为 ESTABLISHED ,之后就可以进行数据传输了;
7)建立连接时的数据包是没有实际内容的,没有应用层的数据;
8)建立连接之后发起的请求数据包,每个数据包都会封装各层协议的头部信息,标志位ACK为1,其他标志位变动;
9)网络进程间的通信,一台服务器内部的进程间通信不用这样;
3、TCP 连接三次握手抓包
1)Socket 在 linux 系统中是一种特殊的文件,因为 linux 系统的理念就是【一切皆文件】,是系统内核级的功能;
2)以上定义比较具体,可以抽象来理解,是一个内核级的用于通信的功能层,包含一组接口函数,这些函数实际就是 *** 作 socket 文件句柄文件描述符;
一个 TCP 连接由四要素【源IP、源Port、目标IP、目标Port】唯一标识,也即 socket 由这四要素唯一确定;
一个 TCP 连接的建立也就是客户端、服务端创建了相对应的一对 socket,客户端和服务端之间的通信也就是这对 socket 间的通信(物理层面是网卡在发送/接收比特流数据);
3) 一个服务与另一个服务建立连接,他们的端口是什么呢 ?
客户端发出请求端口号是随机的,服务端是进程监听的端口号;
2、socket 主要函数介绍
1、进程通信,一个进程只有一个监听 socket,connect socket 是针对一个客户的一个连接的,有很多个; 2、connect 函数内部在发起请求前会找系统随机一个端口号; 3、连接建立后,客户端发起请求传输数据,服务端会直接交给 connect socket 处理,不会交给监听 socket 处理;
4、监听 socket 在处理客户端请求时,如果此时其他客户端发请求过来,监听 socket 是没法处理的,此时系统会维护请求队列由 backlog 参数指定;
全连接队列(completed connection queue)
半连接队列(incomplete connection queue)
Linux 内核 2.2 版本之前 ,backlog 的大小等于全连接队列和半连接队列之和;
Linux 内核 2.2 版本之后 ,backlog 的大小之和全连接队列有关系:
半连接队列大小由 /proc/sys/net/ipv4/tcp_max_syn_backlog 文件指定,可以开很大;
全连接队列大小由 /proc/sys/net/core/somaxconn 文件和 backlog 参数指定,取两个中的最小值;
tomcat acceptCount 就是配置全连接队列大小;
3、socket 函数在建立连接和数据传输的大概使用情况
4、TCP首部结构
1)2的16次方等于 65536,所以系统中端口号的限制个数为 65536,一般1024以下端口被系统占用;
2)标志位这里是 6 个,还有其他标志位的,只是这 6 个标志位常用;
3)seq 序列号,ack 确认序列号,序列号在数据传输时分包用到。三次握手时 seq 序列号是随机的,没有实际意义;
4)TCP 包首部后面接着的是 IP 包首部,再紧接着的是以太网包首部,其实都是加 0101010101 二进制位;
几个常用标志位,首先一个标志位占一个 bit 位,只能是二进制中的 1 或 0;
1)SYN ,简写 S ,请求标志位,用来建立连接。在TCP三次握手中收到带有该标志位的数据包,表示对方想与己方建立连接;
2)ACK ,简写【.】 ,请求确认/应答标志位,用于对对方的请求进行应答,对方收到含该标志位的数据包,会知道己方存在且可用。也会用在连接建立之后,己方发送响应数据给对方的数据包中;
3)FIN ,简写 F ,请求断开标志位,用于断开连接。对方收到己方的含该标志位的数据包,就知道己方想与它断开连接,不再保持连接;
4)RST ,简写 R ,请求复位标志位,因网络或己方服务原因导致有数据包丢失,己方接收到的数据包序列号与上一个数据包的序列号不衔接,那己方会发送含该标志位的数据包告诉对方,对方接收到含该标志位的数据包就知道己方要求它重新三次握手建立连接并重新发送丢失的数据包,一般断点续传会用到该标志位;
还有就是如果对方发过来的数据错了,有问题,己方也会发送含该标志位的数据包;
5)PSH ,简写 P ,推送标志位,表示收到数据包后要立即交给应用程序去处理,不应该放在缓存中,read()/write() 都有缓存区;
6)URG ,简写 U ,紧急标志位,该标志位表示 tcp 包首部中的紧急指针域有效,督促中间层尽快处理;
7)ECE,在保留位中;
8)CWR,在保留位中;
5、TCP 抓包
1)服务端会根据自身情况,没有要处理的数据时会把第二次和第三次挥手合并成一次挥手,此时标志位 FIN=1 / ACK=1;
2)MSL 是 Maximum Segment Lifetime 缩写,指数据包在网络中最大生存时间,RFC 建议是 2分钟;
详细描述:
1)客户端、服务端都可以主动发起断开连接;
2)第一次挥手,客户端向服务端发送含 FIN=1 标志位的数据包,随机序列号 seq=m,此时客户端状态由 ESTABLISHED 变为 FIN_WAIT_1 ;
3)第二次挥手,服务端收到含 FIN=1 标志位的数据包,就知道客户端要断开连接,服务端会向客户端发送含 ACK=1 标志位的应答数据包,确认序列号 ack=m+1,此时服务端状态由 ESTABLISHED 变为 CLOSE_WAIT ;
4)客户端收到含 ACK=1 标志位的应答数据包,知道服务端的可以断开的意思,此时客户端状态由 FIN_WAIT_1 变为 FIN_WAIT_2 ;(第一、二次挥手也只是双方交换一下意见而已)
5)第三次挥手,服务端处理完剩下的数据后再次向客户端发送含 FIN=1 标志位的数据包,随机序列号 seq=n,告诉客户端现在可以真正的断开连接了,此时服务端状态由 CLOSE_WAIT 变为 LAST_ACK ;
6)第四次挥手,客户端收到服务端再次发送的含 FIN=1 标志位的数据包,就知道服务端处理好了可以断开连接了,但是客户端为了慎重起见,不会立马关闭连接,而是改状态,且向服务端发送含 ACK=1 标志位的应答数据包,确认序列号 ack=n+1,此时客户端状态由 FIN_WAIT_2 变为 TIME_WAIT ;
等待 2 个MSL 时间还是未收到服务端发过来的数据,则表明服务端已经关闭连接了,客户端也会关闭连接释放资源,此时客户端状态由 TIME_WAIT 变为 CLOSED ;
也就是说 TIME_WAIT 状态存在时长在 1~4分钟;
7)服务端收到含 ACK=1 标志位的应答数据包,知道客户端确认可以断开了,就立即关闭连接释放资源,此时服务端状态由 LAST_ACK 变为 CLOSED ;
SYN 洪水攻击(SYN Flood)
是一种 DoS攻击(拒绝服务攻击),大概原理是伪造大量的TCP请求,服务端收到大量的第一次握手的数据包,且都会发第二次握手数据包去回应,但是因为 IP 是伪造的,一直都不会有第三次握手数据包,导致服务端存在大量的半连接,即 SYN_RCVD 状态的连接,导致半连接队列被塞满,且服务端默认会发 5 个第二次握手数据包,耗费大量 CPU 和内存资源,使得正常的连接请求进不来;
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)