【Linux网络】详解TCP协议(2)

news2024/12/24 0:33:03
图片名称
🎉博主首页: 有趣的中国人

🎉专栏首页: Linux网络

🎉其它专栏: C++初阶 | C++进阶 | 初阶数据结构

在这里插入图片描述

小伙伴们大家好,本片文章将会讲解 TCP协议的三次握手和四次挥手 的相关内容。


如果看到最后您觉得这篇文章写得不错,有所收获,麻烦点赞👍、收藏🌟、留下评论📝。您的支持是我最大的动力,让我们一起努力,共同成长!

文章目录

  • `1. 三次握手`
    • ==<font color = blue><b>🎧1.1 从代码看三次握手🎧==
    • ==<font color = blue><b>🎧1.2 三次握手的中间过程🎧==
    • ==<font color = blue><b>🎧1.3 三次握手的中间状态🎧==
    • ==<font color = blue><b>🎧1.4 三次握手一定要三次吗🎧==
  • `2. 四次挥手`
    • ==<font color = blue><b>🎧2.1 从代码看四次挥手🎧==
    • ==<font color = blue><b>🎧2.2 四次挥手的中间过程🎧==
    • ==<font color = blue><b>🎧2.3 四次挥手的中间状态🎧==
    • ==<font color = blue><b>🎧2.4 四次挥手的原因🎧==



上一篇文章中,博主介绍了 :

  • TCP 的确认应答机制;
  • TCP 的捎带应答机制;
  • TCP 的超时重传机制;
  • TCP 的报头

建议将上一篇文章看完之后再来看这篇文章,链接如下:

【Linux网络】详解TCP协议(1)

那么接下来正片开始:


1. 三次握手


🎧1.1 从代码看三次握手🎧


我们之前在客户端写的代码有以下几步:

  1. 创建一个套接字;
  2. 进行connect()建立链接,以下是它的接口介绍:
    • int connect (int sockfd, const struct sockaddr *addr,socklen_t addrlen);
  3. 详细代码
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
connect(sockfd, serveraddr, serveraddr_len);

在服务器中写的代码有以下几步:

  1. 分配一个监听套接字;
  2. 绑定监听套接字的源地址和目标地址:bind(),以下是它的接口介绍:
    • int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
  3. 使监听描述符成为一个监听描述符:listen(),以下是它的接口介绍:
    • int listen(int sockfd, int backlog);
  4. 进行 accept() 阻塞等待客户端链接,以下是它的接口介绍:
    • int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags);
  5. 详细代码
int listenfd = socket(AF_INET, SOCK_STREAM, 0);
bind(listenfd, localaddr, localaddr_len);

listen(listenfd, backlog);
int connfd = accept(listenfd, cilentaddr, clientaddr_len, 0);

详细解释 connect() 和 listen()

  • 其实在 connect() 的时候就是 客户端向服务器发送三次握手的开始时间点
  • 服务器会首先处于 listen() 状态(监听状态),准备接受客户端的连接请求;
  • listen() 的底层其实会维护两个队列,一个是已完成连接的队列 ,另一个是还在建立连接的队列。
    • 当客户端与服务器的三次握手完成后,连接将被移入已完成连接的队列,这个队列的大小实际上就是由 l i s t e n ( ) listen() listen() 的第二个参数 b a c k l o g backlog backlog 决定的,如果队列满了,服务器就会直接拒绝请求。
    • 未完成连接队列存放的是那些尚未完成三次握手的连接请求。这些连接请求在被处理之前,会暂时存放在这个队列中。
  • 所以 accept() 是在干什么呢?其实就是从已经完成连接建立的队列中取出一个连接,然后再分配一个文件描述符

🎧1.2 三次握手的中间过程🎧


三次握手过程图解
在这里插入图片描述

TCP 报头中有一个标志位是 SYN,这个标志位在三次握手中起着关键的作用。

  1. 客户端给服务器发送包含 SYN 标志位的报文;
  2. 服务器接收客户端发送的报文,同意建立连接,并发送携带 ACK SYN 的报文(捎带应答);
  3. 客户端接受到报文之后,会给服务器在发送一个 ACK,客户端一旦发送了这个报文,就表示建立连接成功了。
    • 我们知道当接收方接收到发送方的信息的时候会给发送方发送 ACK 数据包,这样发送方就可以确定接收方收到了消息;
    • 那么在三次握手中,最后一次客户端发送的ACK,客户端本身是不能确定服务器一定收到了消息;
    • 所以其实客户端其实是在赌,服务器收到了消息。

详细解释最后一次 ACK 丢包

  • 刚才说了,客户端最后一次的 ACK 服务器可能并没有收到
  • 但是在客户端发送最后一次 ACK 之后,客户端立即把自己的状态变成了 ESTABLISHED
  • 那么就说明客户端此时就可以立即给服务器发送携带正文的数据;
  • 但是如果服务器接收到了消息,但是很明显,服务器的连接状态并没有变成 ESTABLISHED
  • 所以此时,服务器就会给客户端发送一个携带 RST 标志位的报文

RST 标志位

  • 当服务器给客户端发送这个报文的时候,服务器就会提醒客户端重新建立连接;
  • 因此这样就可以完美解决最后一次 ACK 服务器可能没收到的情况。

🎧1.3 三次握手的中间状态🎧


客户端的状态变化:

  • [CLOSED -> SYN_SENT] 客户端调用 connect, 发送同步报文段;
  • [SYN_SENT -> ESTABLISHED] 成功调用 connect , 则进入 ESTABLISHED 状态, 开始读写数据;(成功接受服务器端的 ACK + SYN

服务器的状态变化:

  • [CLOSED -> LISTEN] 服务器端调用 listen 后进入 LISTEN 状态, 等待客户端连接;
  • [LISTEN -> SYN_RCVD] 一旦监听到连接请求(同步报文段), 就将该连接放入内核等待队列中, 并向客户端发送 SYN 确认报文;
  • [SYN_RCVD -> ESTABLISHED] 服务端一旦收到客户端的确认报文, 就进入ESTABLISHED 状态, 可以进行读写数据了。

🎧1.4 三次握手一定要三次吗🎧


三次握手可以是一次吗?

  • 如果三次握手变成一次,就是说只要客户端给服务器发送一次 SYN 请求,服务器就会立马在内核中维护这个连接说明连接建立成功
  • 这个时候如果是某些不法分子利用这个特点,在一台甚至多台主机上向某个服务器发送多个SYN请求,就会造成服务器崩溃;
  • 这个被称为 SYN 洪水SYN 洪水具体的内容博主也不是很清楚,只知道这个在网络安全中被称之为 DoS 攻击,如果各位有兴趣可以去了解一下。

三次握手可以是两次吗?

  • 如果是两次,其实也不行,因为如果客户端对服务器发送的 ACK + SYN 不做处理,只是单纯的让服务器在他的操作系统内部维护连接队列,就依然会引发 SYN 洪水 问题。
  • 所以两次也是不行的,但是其实三次也会有类似的问题,但是这个时候服务器和客户端消耗的资源是类似的,所以少数的主机就很难将服务器挂掉。

为什么一定是三次呢?

  • 首先因为三次握手可以验证网络的连通性,同时验证TCP是全双工的;
    • 因为如果是两次握手只能说明客户端可以发送数据,服务器可以发送、接受数据;
  • 由于双方的地位是相同的,三次握手也可以说明他们彼此之间都想和对方通信,即达成通信共识意愿。
  • 其实三次握手本质上就是四次握手,只是在第二次握手的时候服务器将 ACKSYN 合并成了一条数据包发送而已。

在这里插入图片描述



2. 四次挥手


🎧2.1 从代码看四次挥手🎧


  • 在双方通信完毕之后,就会关闭掉对应的文件描述符,调用 close() 接口,下面是它的详细接口:
    • int close(int fd);
  • 其实在 close() 的时候就会发生两次次挥手,表示要和对方断开连接。
  • 双方都调用 close() 就是四次挥手了。

🎧2.2 四次挥手的中间过程🎧


四次挥手过程图解

在这里插入图片描述
TCP 报头中有一个标志位是 FIN,这个标志位在三次握手中起着关键的作用。

  • 当服务器或者客户端中的一方已经把要发送给对方的数据发送完了,那么这时就会给对方发送一个带有FIN标志位的数据报;
    • 这里断开连接的一方没有特定的规定必须是客户端或者服务器,只要给对方发送的数据发送完了就行。
  • 当对方接受到报文之后就会给对方发送ACK
  • 如果过了一段时间我要发送的数据也给对方发完了,那么我也要给对方发送带有FIN标志位的报文,并且另一端也要给我发送ACK表示收到了我要断开连接的请求。

两次挥手可能不会同时发生

  • 刚才说了如果一方已经把消息发送完了,就会给对方发送带有FIN标志位的数据报,但是这个时候另一方可能还要给我发送数据,所以说这个时候另一方还不想和我断开连接;
  • 那么这个时候另一方还是可以和我发送数据的;
  • 但是我已经把我的文件描述符fd关掉了,也就是这个文件描述符对应的发送和接收缓冲区也就关掉了但是我还是要接受另一方发送的数据的,所以这里就不能单纯的调用close()系统调用;
  • 这里再介绍一个系统调用 shutdown(),下面是这个接口的用法:
    • int shutdown(int sockfd, int how); 这个接口表示我要以何种方式关闭我的文件描述符,何种方式就是how对应的参数,有以下几个选项:
      • SHUT_RD 表示只关闭读端;
      • SHUT_WR 表示只关闭写端;
      • SHUT_RDWR 表示同时关闭读写端,这个时候就和close()类似了。

🎧2.3 四次挥手的中间状态🎧


客户端的状态变化:

  • [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 状态。(这里待会细说)

服务器的状态变化:

  • [ESTABLISHED -> CLOSE_WAIT] 当客户端主动关闭连接(调用 close), 服务器会收到结束报文段, 服务器返回确认报文段并进入 CLOSE_WAIT
  • [CLOSE_WAIT -> LAST_ACK] 进入 CLOSE_WAIT 后说明服务器准备关闭连接(需要处理完之前的数据); 当服务器真正调用 close 关闭连接时, 会向客户端发送FIN, 此时服务器进入 LAST_ACK 状态,等待最后一个 ACK 到来(这个 ACK 是客户端确认收到了 FIN);
  • [LAST_ACK -> CLOSED] 服务器收到了对 FINACK, 彻底关闭连接。

详细解释 CLOSE_WAIT 状态

  • 当服务器或者客户端处于 CLOSE_WAIT 状态,说明只是另一方要给我发送的数据发送完了,但是我还没有把数据发送完毕;
  • 但是如果主机上存在大量的 CLOSE_WAIT 状态,原因就是没有正确的关闭sockfd,导致四次挥手没有正确完成,这是一个 BUG, 只需要加上对应的 close() 即可解决问题。

详细解释 TIME_WAIT 状态(重要)

  • TIME_WAIT 状态一般是先发送退出请求的一方会处于的状态;
  • 这个状态一般有两个作用:
    • 首先就是虽然对方已经发送了FIN请求了,但是在信道中可能还存在有部分数据报并没有到达对方的接受缓冲区,所以这就是为什么要等待 2 ∗ M S L 2 * MSL 2MSL 的原因, MSL 就是 MAX SEGMENT LIFETIME,最大段生存时间;
    • 如果说不等待这个时间,可能会对下一次连接的主机产生影响,会收到来自上一个进程的迟到的数据,会有预想不到的错误;
    • 第二个作用就是对方处于LAST_ACK状态发送了FIN数据报给我,我收到之后会给对方发送ACK我要保证对方收到了我的ACK,以保证链接正确关闭
    • 假设最后一个 ACK 丢失,那么服务器会再重发一个 FIN, 这时虽然客户端的进程不在了, 但是 TCP 连接还在, 仍然可以重发 LAST_ACK
  • 这也就是说为什么我们写一个服务器第一次绑定一个端口,如果关闭服务器之后,第二次立即重新绑定这个端口是不可能的,一般要等待一段时间。
    • 当然我们以可以用setsockopt()这个系统调用解决这个问题,下面是他的一般用法:
      • 接口API:int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);

      • 第一步:int opt = 1;

      • 第二步:setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));在这里插入图片描述

查看TIME_WAIT状态

  • 博主这里用了本地的浏览器去访问了在云服务器写的http的服务器;
  • 然后关闭掉服务器;
  • 在采用 netstat -natp 命令查看。
  • 这里为什么本地地址是12.0.12.12我们之后在讲网络层IP协议的时候会讲,这里其实是公网路由器的IP,这个路由器是公网和内网交互的路由器
    在这里插入图片描述

🎧2.4 四次挥手的原因🎧


  • 如果只是 主机A主机B 发送完了数据,那么 主机A 会给 主机B 首先发送FIN报文;
  • 但是 主机B 还没有发送完数据,因此我还要继续发送数据;
  • 等到发送完了数据,主机B 才会给 主机A 发送FIN数据报;
  • 所以总的来说双方地位平等,都要给对方发送断开连接的请求(FIN)才能完美的断开连接,所以这就是为什么是四次的原因。
  • 但是如果 主机A 发送FIN数据报的时候,主机B 接收到了请求,主机B这个时候也发送完了数据,那么就会给 主机A 同时发送携带FINACK的报文,所以三次和四次挥手本质上没什么不同。

在这里插入图片描述

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

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

相关文章

AT89C51单片机和STC单片机烧录不同引脚问题

首先确定一下&#xff0c;两种烧录接口引脚不同 STC烧录器主要使用串口引脚 实际上stm32中也可以使用这种UART通信方式烧录程序&#xff0c;只是需要确定连接引脚进入bootloader模式 AT89C51来源Atmel公司&#xff0c;其中AVR单片机也是这个公司 ISP和SPI不是一个概念&…

C++不同的头文件中各种函数的操作使用(长期更新,找到新的就补充进来)

一、万能头文件 #include <bits/stdc.h> 万能头文件中包含的内容 // C #ifndef _GLIBCXX_NO_ASSERT #include <cassert> #endif #include <cctype> #include <cerrno> #include <cfloat> #include <ciso646> #include <climits> #in…

智慧城市交通管理中的云端多车调度与控制

城市交通管理中的云端多车调度与控制 智慧城市是 21世纪的城市基本发展方向&#xff0c;为了实现智慧城市建设的目标&#xff0c;人们需要用现代化的手段去管理和控制城市中的各种资源和设施。智能交通控制与管理是智慧城市中不可缺少的一部分&#xff0c;因为现代城市交通系统…

优化|基于深度学习的不动点算子优化的热启动方法

原文信息&#xff08;包括题目、发表期刊、原文链接等&#xff09;&#xff1a;Learning to Warm-Start Fixed-Point Optimization Algorithms 原文作者&#xff1a;Rajiv Sambharya, Georgina Hall, Brandon Amos, and Bartolomeo Stellato 论文解读者&#xff1a;陈宇文 编…

C++模版类实现栈

text.h #ifndef TEXT_H #define TEXT_H#include <stdexcept> // 用于 std::out_of_rangetemplate <typename T> class MyStack { private:T* data; // 指向底层数组的指针int capacity; // 容量int top; // 栈顶索引int size; // 当前元…

了解Webpack并处理样式文件

目录 引入定义安装和使用配置文件命令配置单独文件指定文件 处理样式css-loader使用 style-loaderless-loaderPostCSSpostcss-loaderpostcss-preset-env 引入 随着前端的快速发展&#xff0c;目前前端的开发已经变的越来越复杂了&#xff1a; 比如开发过程中我们需要通过模块化…

物联网系统中高精度压力检测方案_压力变送器

01 物联网系统中为什么要使用压力变送器 在物联网系统中使用压力变送器的原因主要基于以下几个方面&#xff1a; 感知层的核心作用 物联网系统主要由感知层、传输层、平台层和应用层组成。感知层作为物联网的“排头兵”&#xff0c;负责收集物理世界中的各种信息。压力变送…

十大排序算法总结

完整文档见 排序算法总结——语雀文档 比较类排序&#xff1a;通过比较来决定元素间的相对次序&#xff0c;由于其时间复杂度不能突破O(nlogn)&#xff0c;因此也称为非线性时间比较类排序。 非比较类排序&#xff1a;不通过比较来决定元素间的相对次序&#xff0c;它可以突破…

李宏毅机器学习2022-HW9--Explainable AI

Task CNN explanation 11种食物图片分类&#xff0c;与HW3使用同一个dataset Bread, Diary product, Dessert, Egg, Fried food, Meat, Noodles/Pasta, Rice, Seafood, Soup, and Vegetables/Fruit 训练一个CNN model用于classification&#xff0c;并做一些explanations …

一站式家装服务管理系统

摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本一站式家装服务管理系统就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在短时间内处理完毕庞大的数…

可商业化的数字孪生系统

可商业化的数字孪生系统 相关链接 演示地址 演示地址 更多获取 更多获取 源码地址 源码地址 数字孪生系统 数字孪生系统&#xff08;Digital Twin System&#xff09;是新一代科技赋能的核心技术&#xff0c;通过虚拟与现实的双向映射&#xff0c;实现对物理世界的全面感…

GAMES101(17~18节,物理材质模型)

材质 BRDF 材质&#xff1a;决定了光线与物体不同的作用方式 BRDF定义了物体材质,包含漫反射和镜面部分 BSDF &#xff08;scattering散射&#xff09; BRDF&#xff08;reflect反射&#xff09; BTDF 光线打击到物体上会向四面八方散射 反射 光线打击到物体上反射出去…

基于Java+SQL Server2008开发的(CS界面)个人财物管理系统

一、需求分析 个人财务管理系统是智能化简单化个人管理的重要的组成部分。并且随着计算机技术的飞速发展&#xff0c;计算机在管理方面应用的旁及&#xff0c;利用计算机来实现个人财务管理势在必行。本文首先介绍了个人财务管理系统的开发目的&#xff0c;其次对个人财务管理…

【C语言】指针篇 | 万字笔记

写在前面 在学习C语言过程&#xff0c;总有一个要点难点离不开&#xff0c;那就是大名鼎鼎的C语言指针&#xff0c;也是应为有指针的存在&#xff0c;使得C语言一直长盛不衰。因此不才把指针所学的所有功力都转换成这个笔记。希望对您有帮助&#x1f970;&#x1f970; 学习指…

【STM32开发环境搭建】-1-Keil(MDK) 5.27软件安装和注册教程

目录 1 安装前装备工作 2 安装KEIL(MDK-ARM) 5.27软件 3 注册KEIL(MDK-ARM) 5.27软件&#xff0c;获取License许可证 4 手动安装STM32F0&#xff0c;STM32F1&#xff0c;STM32F4&#xff0c;STM32F7&#xff0c;STM32H7的支持包 4.1 下载STM32的支持包 4.2 安装STM32的支…

JavaScript 中变量命名的最佳实践

全篇大概1500 字&#xff08;含代码&#xff09;&#xff0c;建议阅读时间5分钟。 1. 避免使用 var 关键字&#xff1a;过时的产物 在现代 JavaScript 中&#xff0c;我们通常避免使用 var&#xff0c;而是选择 let 和 const&#xff0c;它们提供更可预测和块范围的行为&#x…

C++初阶:STL详解(七)——list的模拟实现

✨✨小新课堂开课了&#xff0c;欢迎欢迎~✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;C&#xff1a;由浅入深篇 小新的主页&#xff1a;编程版小新-CSDN博客 前言&#xff1a; 我们前面已经了解到了lis…

helm3 部署项目应用示例

一、用到的插件 1、存储卷-日志外挂&#xff1a; 存储类自己提前建 2、env变量-存储nacos信息 二、新建项目 # helm create test-gateway 三、修改values.yaml ## 删除内容 # Additional volumes on the output Deployment definition. volumes: [] # - name: foo # se…

助力智能作物植株统计分析,基于YOLOv7全系列【tiny/l/x】参数模型开发构建田间作物场景下智能精准小麦麦穗检测识别计数系统

农业实验研究的一些场景下&#xff0c;尝尝有对指定视野区域内作物植株数量进行便捷化智能自动化统计计数的需求&#xff0c;诸如&#xff1a;棉花植株统计、小麦植株统计、水稻植株统计等等&#xff0c;这些农业实验场景下&#xff0c;单纯依靠人工数数的方式来进行植株计数是…

SpringBoot--yml配置文件的时间/大小的单位转换

原文网址&#xff1a;SpringBoot--yml配置文件的时间/大小的单位转换_IT利刃出鞘的博客-CSDN博客 简介 说明 本文介绍SpringBoot的yml&#xff08;properties&#xff09;配置文件的时间/大小的单位转换。 概述 SpringBoot可以将yml中的配置绑定到一个Java类的字段&#x…