IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> TCP三次握手与四次挥手 -> 正文阅读

[网络协议]TCP三次握手与四次挥手

1. 三次握手(建立连接)

(1) 客户端发送一个 SYN 段(SYN 标志位置位),包含初始序号 ISN,在图 中,这个序号的值 seq = 2379453243. 在这个过程中,客户端是通过 connect 函数发起连接请求的,此时 connect 函数阻塞,等待服务器发回 ACK 应答。

(2) 服务器端接收到 SYN 段后(listen),知道有新的连接请求到来,于是初始化一个序号 ISN,在上面的例子中这个值是 seq = 4269857. 此时服务器创建一个 TCP 段,将 SYN 和 ACK 标志置位,让 seq = 4269857, ack = 2379453244,然后将这个 TCP 段发送给客户端。这个步骤完全有内核完成,并将此连接加入未完成连接队列。

(3) 客户端再次收到服务器发送来的 TCP 段后,检查到带有 SYN 和 ACK 标志,于是客户端一方连接已经建立成功,此时 connect 函数返回。客户端创建一个 TCP 段,将 ACK 标志置位,同时将 ack 的值设置为 4269858,发送给服务器。

(4) 服务器收到客户端的 ACK 后,将此连接移到已完成连接队列(accept)。

三次握手是有效的

先解释建立连接的一个基础条件:假设 A 和 B 要建立连接,A 给 B 发送 SYN,直到 A 确定下来 B 已经收到,可以用 ACK + 超时重传机制来实现。

同样的,B 也可以利用 ACK + 超时重传机制给 A 发送 SYN 段。

这样双方都可以保证对方收到了自己的 SYN 段。写成步骤就是下面这样:

(1) A ------ SYN ------> B

(2) B ------ ACK ------> A

(3) B ------ SYN ------> A

(4) A ------ ACK ------> B

聪明的同学一眼就可以看出来步骤 2 和 3 可以合并成一个。因此,三次握手是有效的,正确的。

为什么要三次握手?

为什么要三次握手,而不是两次,即为什么客户端需要第三次握手发送ACK??

理论上,A发送SYN后,B发送ACK,A收到ACK就代表双向连接是通的。可为什么A在收到ACK后需要回一次ACK给B?

从两方面解释:

(1) 防止某个在网络中滞留的 SYN 段突然又被传送到服务器端

在谢希仁著《计算机网络》第四版中讲“三次握手”的目的是“为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误”。

假设某上在网络中滞留的 SYN 突然被传送到服务器端,服务器收到此 SYN,必然认为是有客户发起了请求,于是回复 SYN + ACK. 这对应于前面讲的步骤 2 和 3,接下来服务器就需要等待对端回复的 ACK,对应于步骤 4.

实际上,根本就不存在这样的一个客户端,因为 SYN 段只是一个滞留数据段,客户端早已不存在,因此服务器永远等不到对端的 ACK 回复,经过一次次数的超时重传,服务器最终会放弃这个连接。

显然,这种滞留的 SYN 会极大的浪费服务器资源。SYN 泛洪攻击服务器就是利用的这个原理。

(2)?确认双方的初始序号 —— ISN

有些同学会有疑问,即使没有三次握手,就不能利用 ACK + 超时重传发送数据了吗,也就是说使用 UDP 协议 + ACK 能不能完成 TCP 的功能?

不妨这样看,你按顺序发了两个 UDP 数据报,假设为数据报 1 和 2,而对端先收到数据报 2,其次才收到数据报 1. 这时候,对端凭什么才能知道哪个数据报应该在前,哪个在后呢?在你不解释的情况下,对端就认为数据报 2 排在前面,数据报 1 排在后面。

为了解决这个问题,你不得不在你的 UDP 报文里加入编号,对端收到了这个报文,就可以自己根据报文编号排序了。好了,这个问题也解决了,还有一个问题,如何才能告诉对方,哪个 udp 报文是第一个报文?

假设你连续发送了 1 、2、3、4、5 号报文,对方收到了3、4、5,还有 1 和 2 对方暂时还没收到,你说,对端能不能把 3、4、5 交付给应用程序,虽然 3、4、5 的顺序没错?显然不行,你还得告诉对方哪个报文是第一个报文,是不是?然后你又加了一个标记在报文 1 里,当对方收到报文 1 了,就知道 1 号报文是第一个报文了,于是就按照顺序从 1 号报文开始,把数据交付给上层应用程序了。

上面的过程,也就是确定初始报文序号的过程,可以单独提炼出来。为了这样就简化了程序的设计,不妨把确定初始报文序号的过程在一开始就确定下来,只要没确定下来,就不发数据。对应到三次握手,本质上就是确定 ISN,即初始序号。

因此,三次握手的目的之一,即建立 TCP 连接的目的,是为了确认双方的初始序号 —— ISN.

再来一些补充,有同学还会问,双方约定起始报文序号就是 1 不就好了?没错,确实可以,但是这样不安全,黑客非常容易构造出一个合法序号的 tcp 报文对你进行攻击。因此 ISN 在一般会选取一个随机数,这取决于你的协议栈具体实现。

2. 四次挥手(断开连接)

客户端主动关闭连接 —— TCP 四次挥手

  • 客户端打算关闭连接,此时会发送一个 TCP 首部?FIN?标志位被置为?1?的报文,也即?FIN?报文,之后客户端进入?FIN_WAIT_1?状态。

  • 服务端收到该报文后,就向客户端发送?ACK?应答报文,接着服务端进入?CLOSED_WAIT?状态。

  • 客户端收到服务端的?ACK?应答报文后,之后进入?FIN_WAIT_2?状态。

  • 等待服务端处理完数据后,也向客户端发送?FIN?报文,之后服务端进入?LAST_ACK?状态。

  • 客户端收到服务端的?FIN?报文后,回一个?ACK?应答报文,之后进入?TIME_WAIT?状态

  • 服务器收到了?ACK?应答报文后,就进入了?CLOSED?状态,至此服务端已经完成连接的关闭。

  • 客户端在经过?2MSL?一段时间后,自动进入?CLOSED?状态,至此客户端也完成连接的关闭。

(1) 首先,由客户端调用 close,将这一端称为主动关闭(active close)。然后该端发送一个 FIN 段到对端。

(2) 接收到 FIN 段的服务器执行被动关闭(passive close)。接下来,接收到此 FIN 段的服务器回复 ACK 进行确认(实际上是由内核自动完成回复的),同时内核会传递一个文件结束符 EOF(放在缓冲区末尾) 给应用进程,应用进程read会读取到EOF表示发送端连接已关闭。

(3) 一段时间后……服务器端也没有数据要发送给对端了,调用 close,这导致服务器端也发送一个 FIN 段到对端。

(4) 客户端接收到 FIN 后,回复 ACK 进行确认。

注意:

执行主动关闭的一方既可以是客户端,也可以是服务器,这两者之间是对等的。

通常断开连接需要 4 个 TCP 段。但是有时候不一定是这样。某些情况下,步骤 1 中的 FIN 段会随着数据一起发送到对端;另一种情况,步骤 2 和步骤 3 有可能被合并成一个 TCP 段

为什么是四次挥手?

实际上,这是 TCP 的半关闭(half-close)特性所造成的。

因为 TCP 连接是全双工的(数据在两个方向上可以同时传递),因此每个方向就必须能够单独的关闭。就比如客户端执行了半关闭操作后,只是通知服务器它没有数据要发送了,并不代表它不能接收数据。因此,只要服务器还没有主动关闭,就能够向客户端继续发送数据。也就相当于在步骤 2 和 步骤 3 之间,服务器仍然能够向客户端发送数据。

四次挥手为被动关闭的一方提供了很大的伸缩空间,让被动关闭一方有机会继续向主动关闭一方发数据。如果TCP 协议标准要求步骤 2 和 步骤 3 合并成一个步骤,这种伸缩空间就没有了,也就是说只要有一端关闭了,另一方就没有机会继续发送数据。

应用层如何知道对端已经调用close?

当read(socketfd,buffer,n)返回0时代表对端已经调用close

server端调用了close(soketfd)函数

server调用了close(fd,SHUT_WR),关闭写连接,半关闭

【注意】

在 Linux 编程学习笔记中讲网络编程的时候,我们关闭连接用的都是函数 close. 任何一端使用函数 close 进行关闭,实际上就直接把全双工两个方向的连接全部关闭了。如果只想关闭一端,只能使用函数 shutdown 来关闭一个方向的连接。

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2021-07-10 14:49:19  更:2021-07-10 14:49:42 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/27 14:55:10-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码
数据统计