Linux网络:传输层协议TCP(二)三次挥手四次握手详解

目录

一、TCP的连接管理机制

1.1三次握手 

 1.2四次挥手

二、理解 TIME_WAIT 状态

2.1解决TIME_WAIT 状态引起的 bind 失败的方法

三、理解CLOSE_WAIT状态

 


一、TCP的连接管理机制

在正常情况下, TCP 要经过三次握手建立连接, 四次挥手断开连接

1.1三次握手 

三次握手顾名思义就是在刚开始建立连接时,一端向另一端发送SYN连接请求(一般时客户端向服务器),服务器收到后做出应答,也向客户端发送SYN连接请求顺便捎带ACK应答,客户端收到后再次向服务器发送ACK应答,至此三次握手完成,双方连接建立完毕。

前两次握手只要有一次没有成功那么连接都是无法建立的双方都是可以清楚的知道连接是没有建立的,那如何保证第三次握手的ACK对方收到了呢?

一般情况下如果前两次握手成功,第三次出现失败的概率很小,如果真的出现,比如客户端给服务器发的携带ACK应答的报头在中途丢包了,那么此时服务器就会进入超时等待,那么此时客户端会认为连接已经建立好了,可能就会直接进行数据报文的传输,此时服务器收到报文,但是对于服务器端,连接还未建立好,此时服务器就会对客户端做出应答,将报头中的RST置为1,表示重置。

此时就会进行重置,双方重新进行三次握手。所以RST主要就是解决连接问题。

所以为什么要三次握手?

1、因为建立连接维护连接是有成本的,如果连接只需要单次握手就能建立,那客户端疯狂的单方面给服务器发送连接最终就会导致服务器花费资源来维护大量连接,这种情况服务器是很容易被攻击的。而这种大量发送SYN的情况,我们也将其称为SYN洪水攻击。而三次握手我们可以发现,客户端也需要进行一次接收和一次应答,付出的代价和服务器是对等的,而在上图的逻辑中,服务器也就倒逼着客户端,服务器和客户端建立连接的前提是客户端先建立连接!所以三次握手虽然不能保证绝对的安全,但是起码可以避免遭受单机攻击就被搞挂掉,这也是为什么不一次两次握手的主要理由。

2、三次握手,client和server双方,都会有确定的一次收发,三次握手也是最小成本确认全双工的方式,确保双方OS是健康且愿意通信的,其实三次握手本质上也是4次握手,只不过中间两次被变成一次捎带应答了。

 1.2四次挥手

一般情况下,依旧是客户端率先起手发送携带FIN的报头,表示断开连接的请求,此时是第一次挥手 ,当客户端发送携带FIN标志位的报头时 ,此时就表明用户层不会再有数据往发送缓冲区进行写入了,因为FIN往往就对应我们使用的close(fd),此时我们在代码中调用了close关闭了对应的文件描述符,那么我们就肯定无法再往对应的fd中去进行写入了。同样的对于服务器端也是一样的,所以双方发送FIN本质上也就是在用户层调用close(),去将对应缓冲区的数据冲刷干净。

而四次挥手,也是双方OS自动完成的(写到这里博主真的是有苦在心口难开啊,明明不需要博主去实现但还是要拿出100%的精力去学,因为唯有真正了解,才能运用自如啊!)。 而这里为什么不使用捎带应答将两次挥手变成一次呢?

因为与建立连接不同,建立连接时双方都是处于空闲,且目的都是与彼此建立连接,而断开时,客户端数据发送完毕调用close,而服务器此时不一定将所有的数据都完整传输给客户端了,所以无法直接无脑二合一,因此才要分为四次。而close也只是用户层的我们将文件描述符关闭,实际上,真正关闭也是等到对方的ACK以后才正式关闭,也可以调用shutdown来选择性关闭文件描述符的读/写功能。

三次握手与四次挥手就像一场凄美的爱情一样,在一起时总是迫不及待,轰轰烈烈,两步并一步奔向彼此,对未来充满向往,而最终因为现实也好,缘尽也罢,终会有一方先挥手作别,尽管再依依不舍,一次次的wait换来的还是双方的分离。

 服务端状态转化:

[CLOSED -> LISTEN] 服务器端调用 listen 后进入 LISTEN 状态, 等待客户端连接;

[LISTEN -> SYN_RCVD] 一旦监听到连接请求(同步报文段), 就将该连接放入 内核等待队列中, 并向客户端发送 SYN 确认报文.

[SYN_RCVD -> ESTABLISHED] 服务端一旦收到客户端的确认报文, 就进入 ESTABLISHED 状态, 可以进行读写数据了.

[ESTABLISHED -> CLOSE_WAIT] 当客户端主动关闭连接(调用 close), 服务 器会收到结束报文段, 服务器返回确认报文段并进入 CLOSE_WAIT;

[CLOSE_WAIT -> LAST_ACK] 进入 CLOSE_WAIT 后说明服务器准备关闭连接(需要处理完之前的数据); 当服务器真正调用 close 关闭连接时, 会向客户端发送 FIN, 此时服务器进入 LAST_ACK 状态, 等待最后一个 ACK 到来(这个 ACK 是客户 端确认收到了 FIN)

[LAST_ACK -> CLOSED] 服务器收到了对 FIN 的 ACK, 彻底关闭连接.

客户端状态转化:

[CLOSED -> SYN_SENT] 客户端调用 connect, 发送同步报文段;

[SYN_SENT -> ESTABLISHED] connect 调用成功, 则进入 ESTABLISHED 状 态, 开始读写数据; [ESTABLISHED -> FIN_WAIT_1] 客户端主动调用 close 时, 向服务器发送结 束报文段, 同时进入 FIN_WAIT_1;

[FIN_WAIT_1 -> FIN_WAIT_2] 客户端收到服务器对结束报文段的确认, 则进入 FIN_WAIT_2, 开始等待服务器的结束报文段;

[FIN_WAIT_2 -> TIME_WAIT] 客户端收到服务器发来的结束报文段, 进入 TIME_WAIT, 并发出 LAST_ACK;

[TIME_WAIT -> CLOSED] 客户端要等待一个 2MSL(Max Segment Life, 报文 最大生存时间)的时间, 才会进入 CLOSED 状态.

二、理解 TIME_WAIT 状态

现在做一个测试,首先启动 server,然后启动 client,然后用 Ctrl-C 使 server 终止,这时马 上再运行 server, 结果是:

 这是因为,虽然 server 的应用程序终止了,但 TCP 协议层的连接并没有完全断开,因此不 能再次监 听同样的server 端口. 我们用 netstat 命令查看一下:

 • TCP 协议规定,主动关闭连接的一方要处于 TIME_ WAIT 状态,等待两个 MSL(maximum segment lifetime)的时间后才能回到 CLOSED 状态.

我们使用 Ctrl-C 终止了 server, 所以 server 是主动关闭连接的一方, 在 TIME_WAIT 期间仍然不能再次监听同样的 server 端口;

MSL 在 RFC1122 中规定为两分钟,但是各操作系统的实现不同, 在 Centos7 上 默认配置的值是 60s;

可以通过 cat /proc/sys/net/ipv4/tcp_fin_timeout 查看 msl 的值;

想一想, 为什么是 TIME_WAIT 的时间是 2MSL?

MSL 是 TCP 报文的最大生存时间, 因此 TIME_WAIT 持续存在 2MSL 的话

就能保证在两个传输方向上的尚未被接收或迟到的报文段都已经消失(否则服 务器立刻重启, 可能会收到来自上一个进程的迟到的数据, 但是这种数据很可能是错 误的);

同时也是在理论上保证最后一个报文可靠到达(假设最后一个 ACK 丢失, 那么 服务器会再重发一个 FIN. 这时虽然客户端的进程不在了, 但是 TCP 连接还在, 仍然 可以重发 LAST_ACK);

2.1解决TIME_WAIT 状态引起的 bind 失败的方法

在 server 的 TCP 连接没有完全断开之前不允许重新监听, 某些情况下可能是不合理的

服务器需要处理非常大量的客户端的连接(每个连接的生存时间可能很短, 但是 每秒都有很大数量的客户端来请求).

这个时候如果由服务器端主动关闭连接(比如某些客户端不活跃, 就需要被服务 器端主动清理掉), 就会产生大量 TIME_WAIT 连接.

由于我们的请求量很大, 就可能导致 TIME_WAIT 的连接数很多, 每个连接都会 占用一个通信五元组(源 ip, 源端口, 目的 ip, 目的端口, 协议). 其中服务器的 ip 和端 口和协议是固定的. 如果新来的客户端连接的 ip 和端口号和 TIME_WAIT 占用的链 接重复了, 就会出现问题.

使用 setsockopt()设置 socket 描述符的 选项 SO_REUSEADDR 为 1, 表示允许创建端 口号相同但 IP 地址不同的多个 socket 描述符

三、理解CLOSE_WAIT状态

当你看到CLOSE_WAIT状态时,说明此时只是断开了一个方向上的连接,四次挥手并没有完成。下面可以做一个实验,我们让客户端主动断开连接,服务端处在一个死循环中或者服务端不调用close函数。

假设现在客户端和服务端建立好连接,服务端一旦建立连接服务端到客户端方向上的连接,就会处于ESTABLISHED状态。

对于服务器上出现大量的 CLOSE_WAIT 状态, 原因就是服务器没有正确的关闭 socket, 导致四次挥手没有正确完成. 这是一个 BUG. 只需要加上对应的 close 即可解决问题。

服务端结束服务以后一定要调用close函数,否则会发生内存泄漏

发生CLOSE_WAIT 的原因,大致有两点猜想:

  • 没有处理 read 返回值为  0 的情况。一般read 返回值为0 ,认为对端连接断开;read 返回值小于0,说明读取出错。
  • 主要原因可能还是客户端关闭了连接,然而此时服务端忙于处理其他 IO ,没有关闭连接。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/48773.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Docker从零开始:安装、部署到卸载,一文搞定全流程

Docker是一种开源容器化平台,它允许开发者将应用程序及其依赖打包成轻量级、可移植的容器。这些容器能确保软件在任何环境中稳定运行,无论是开发者的笔记本电脑还是生产服务器。Docker流行的原因在于其提供的隔离性、可移植性和可扩展性,它简…

2024年展望:人工智能领域将呈现怎样的发展趋势?

2024年,人工智能(AI)领域将继续保持强劲的发展势头,并呈现出多个重要的发展趋势。以下是对该领域未来发展趋势的详细展望: 一、技术创新与融合 多模态生成式AI的崛起: 多模态生成式AI系统能够处理文本、声…

C# 将字符串数组以树型结构化

例如字符串数组: string[] arr { "1","3-4-5-6-7", "2","3-4","3-4-5","3-4-5-6", "3", "6", "4", "6-1", "6-2", "5", "6-1-1&…

李艳波医生怎么挂号?

对于想要预约李艳波医生的患者来说,北京仁爱堂提供了两种便捷的预约方式:来院面诊和视频会诊。来院面诊是传统的就诊方式,患者可以直接前往仁爱堂,与李艳波医生面对面交流,详细了解自己的病情并接受专业的治疗建议。这…

解决Github Copilot无效,无法使用的问题

如果是在Copilot的终端报错 Invalid copilot token: missing token: 403 原因有三种 1,你的账号没有订阅正版的服务,解决办法是购买正版服务 2,你在购买服务的时候,Github上 billing information 地址信息和支付卡片的地址信息不…

关卡1-3:Git

关卡1-3:Git Git基础fork并拉取本次课程的源创建一个gitee自己的仓库 这个是internLM的3期训练营的通关笔记。 任务: 熟悉git熟悉使用git托管平台,常见有github、giteefork官方的训练营的教程项目,提交文件到自己的项目&#xf…

openGauss触发器详解

openGauss 是一款开源关系型数据库管理系统,广泛应用于企业级应用中。随着数据量的增长和业务逻辑的复杂化,数据库管理和操作的自动化需求越来越高。触发器(Triggers)作为数据库中重要的编程工具,能够极大地简化复杂操…

【python】OpenCV—Point Polygon Test

文章目录 1、完整代码2、涉及到的库cv2.pointPolygonTestcv2.minMaxLoc 1、完整代码 from __future__ import print_function from __future__ import division import cv2 as cv import numpy as np # Create an image r 100 src np.zeros((4*r, 4*r), dtypenp.uint8) # 创…

前端学习3——自学习梳理

1.学习一下盒子模型(盒子就是元素&#xff0c;标签) 盒子模型又分为4种&#xff1a;块级&#xff0c;内联级&#xff0c;内联块级&#xff0c;弹性盒子 (弹性盒子续在下一节) 2.元素的结构 1.盒子模型 <!DOCTYPE html> <html lang"en"> <head>&l…

【C++杂货铺】智能指针

目录 &#x1f308; 前言&#x1f308; &#x1f4c1; 内存泄漏 &#x1f4c2; 概念 &#x1f4c2; 分类 &#x1f4c2; 如何避免 &#x1f4c1; RAII &#x1f4c1; C11智能指针 &#x1f4c2; auto_ptr &#x1f4c2; unique_ptr &#x1f4c2; shared_ptr &#x1…

电子电器架构 --- 智能汽车的大脑(域控制器)

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明自己,无利益不试图说服别人,是精神上的节…

QT--进程

一、进程QProcess QProcess 用于启动和控制外部进程&#xff0c;管理其输入输出流。 使用方法 start()&#xff1a;启动一个新进程。setStandardInputFile()&#xff1a;将文件作为标准输入。将进程的标准输入&#xff08;stdin&#xff09;重定向到指定的文件。换句话说&am…

AV1技术学习:Constrained Directional Enhancement Filter

CDEF允许编解码器沿某些(可能是倾斜的)方向应用非线性消阶滤波器。它以88为单位进行。如下图所示&#xff0c;通过旋转和反射所示的三个模板来定义八个预设方向。 Templates of preset directions and their associated directions. The templates correspond to directions of…

MATLAB: ode45 求解常微分方程

引入 ode45 是 MATLAB 中用于求解非刚性常微分方程&#xff08;ODE&#xff09;的数值方法。它基于 Runge-Kutta 方法&#xff0c;并具有自适应步长调整机制&#xff0c;能够在一定误差控制范围内高效地计算 ODE 的数值解。 下面我们通过这个包含详细注释的代码&#xff0c;一…

Windows 11 系统对磁盘进行分区保姆级教程

Windows 11磁盘分区 磁盘分区是将硬盘驱动器划分为多个逻辑部分的过程&#xff0c;每个逻辑部分都可以独立使用和管理。在Windows 11操作系统中进行磁盘分区主要有以下几个作用和意义&#xff1a; 组织和管理数据&#xff1a;分区可以帮助用户更好地组织他们的数据&#xff0c…

无人机之降落操作及紧急情况处理

一、无人机降落操作 1、选择降落地点 a.提前选择一个平坦且没有障碍物的降落点&#xff1b; b.确认降落点周围没有行人或障碍物&#xff0c;保证降落的安全性。 2、降低飞行高度 a.缓慢降低飞行高度&#xff0c;尽量保持匀速下降&#xff0c;防止因下降过快导致无人机受损…

Day20 | 39. 组合总和 40.组合总和II 131.分割回文串

语言 Java 39. 组合总和 组合总和 题目 给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target &#xff0c;找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 &#xff0c;并以列表形式返回。你可以按 任意顺序 返回这些组合。 candidate…

最新可用度盘不限速后台系统源码_去授权开心版

某宝同款度盘不限速后台系统源码&#xff0c;验证已被我去除&#xff0c;两个后端系统&#xff0c;账号和卡密系统 第一步安装宝塔&#xff0c;部署卡密系统&#xff0c;需要环境php7.4 把源码丢进去&#xff0c;设置php7.4&#xff0c;和伪静态为thinkphp直接访问安装就行 …

qt 如何制作动态库插件

首先 首先第一点要确定我们的接口是固定的&#xff0c;也就是要确定 #ifndef RTSPPLUGIN_H #define RTSPPLUGIN_H #include "rtspplugin_global.h" typedef void (*func_callback)(uint8_t* data,int len,uint32_t ssrc,uint32_t ts,const char* ipfrom,uint16_t f…

【前端学习笔记】CSS基础一

一、什么是CSS 1.CSS 介绍 CSS&#xff08;Cascading Style Sheets&#xff0c;层叠样式表&#xff09;是一种用来控制网页布局和设计外观的样式语言。它使得开发者可以分离网页的内容&#xff08;HTML&#xff09;和表现形式&#xff08;样式&#xff09;&#xff0c;提高了…