TCP为什么是三次握手,而不是二次?

为什么需要三次握手?

想象一下,你要给远方的朋友寄一份重要文件。你会怎么做?

普通人的做法: 直接扔进邮箱,祈祷别丢了
聪明人的做法: 先打电话确认地址,再发快递,最后确认收到

TCP的三次握手就是"聪明人的做法"。在茫茫网络中,两台计算机要建立可靠连接,必须先"对暗号",确保双方都准备好了,才能开始传输重要数据。

什么是三次握手?

三次握手(Three-Way Handshake) 是TCP协议建立连接的标准流程,就像两个人见面前的三句对话:

  1. 第一次握手: “你好,我是小明,能听到吗?”
  2. 第二次握手: “听到了,我是小红,你能听到我吗?”
  3. 第三次握手: “能听到,咱们开始聊吧!”

这三步走完,双方就确认了:

  • 我能发消息给你 ✓
  • 你能发消息给我 ✓
  • 我们都准备好了 ✓

生活中随处可见的握手

浏览器访问网站

当你在浏览器输入 www.bd.com 时:

你的浏览器 → 百度服务器:"我想访问你的网站"
百度服务器 → 你的浏览器:"可以,我准备好了,你准备好了吗?"
你的浏览器 → 百度服务器:"我也准备好了,开始传输网页吧!"

手机App联网

打开微信、抖音等App时,底层都在进行三次握手:

  • App客户端发起连接请求
  • 服务器确认并询问客户端状态
  • 客户端确认,开始数据传输

在线游戏连接

玩王者荣耀时的"正在连接服务器",实际上就是三次握手在工作!

深入理解握手机制

核心流程图解

在这里插入图片描述

关键参数解析

SYN (Synchronize):同步标志位

  • SYN=1 表示这是一个连接请求或连接确认报文

ACK (Acknowledgment):确认标志位

  • ACK=1 表示确认号字段有效

seq(序列号):数据包的序号

  • 用于保证数据传输的顺序性

ack(确认号):期望收到的下一个数据包序号

  • ack = 收到的seq + 1

状态变迁详解

客户端状态变化:
CLOSED → SYN_SENT → ESTABLISHED服务端状态变化:  
CLOSED → LISTEN → SYN_RECEIVED → ESTABLISHED

扩展篇:深度思考

为什么是三次,不是两次或四次?

两次握手的问题:

场景:网络延迟导致的重复连接请求1. 客户端发送连接请求A(因网络问题延迟)
2. 客户端以为失败,重新发送请求B
3. 服务端先收到B,建立连接,正常通信后关闭
4. 延迟的请求A到达,服务端又建立连接
5. 但客户端已经不需要了,造成资源浪费!

三次握手解决方案:
第三次握手让客户端确认这是自己想要的连接,避免了旧连接请求造成的问题。

四次握手: 没必要,三次已经足够确认双方通信能力。

三次握手的安全漏洞

SYN洪水攻击(SYN Flood):

// 攻击原理模拟(仅用于理解,切勿用于实际攻击)
for(int i = 0; i < 10000; i++) {// 发送大量SYN请求,使用虚假IPsendSynPacket(targetServer, fakeIP);// 服务器等待第三次握手,资源被耗尽
}

防护措施:

  • SYN Cookie技术
  • 连接超时机制
  • 防火墙过滤

代码实现:模拟三次握手

Java Socket实现

客户端代码:

public class TCPClient {public static void main(String[] args) {try {// 创建Socket,这里会自动进行三次握手Socket socket = new Socket("127.0.0.1", 8080);System.out.println("🤝 三次握手完成,连接建立!");// 发送数据PrintWriter out = new PrintWriter(socket.getOutputStream(), true);out.println("Hello Server!");// 接收响应BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));String response = in.readLine();System.out.println("服务器响应:" + response);socket.close();} catch (IOException e) {System.err.println("连接失败:" + e.getMessage());}}
}

服务端代码:

public class TCPServer {public static void main(String[] args) {try {ServerSocket serverSocket = new ServerSocket(8080);System.out.println("🚀 服务器启动,等待连接...");while (true) {// accept()方法会完成三次握手的服务端部分Socket clientSocket = serverSocket.accept();System.out.println("🤝 新客户端连接建立!");// 处理客户端请求handleClient(clientSocket);}} catch (IOException e) {System.err.println("服务器错误:" + e.getMessage());}}private static void handleClient(Socket socket) {try {BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));PrintWriter out = new PrintWriter(socket.getOutputStream(), true);String message = in.readLine();System.out.println("收到消息:" + message);out.println("Hello Client! 消息已收到");socket.close();} catch (IOException e) {System.err.println("处理客户端错误:" + e.getMessage());}}
}

底层原理模拟

// 模拟TCP三次握手的核心逻辑
public class HandshakeSimulator {static class TCPPacket {boolean SYN;boolean ACK;  int seq;int ack;public TCPPacket(boolean syn, boolean ackFlag, int seqNum, int ackNum) {this.SYN = syn;this.ACK = ackFlag;this.seq = seqNum;this.ack = ackNum;}@Overridepublic String toString() {return String.format("SYN=%s, ACK=%s, seq=%d, ack=%d", SYN, ACK, seq, ack);}}public static void main(String[] args) {Random random = new Random();int clientSeq = random.nextInt(1000);int serverSeq = random.nextInt(1000);System.out.println("🌟 TCP三次握手模拟开始");System.out.println("=" * 50);// 第一次握手:客户端发送SYNTCPPacket syn = new TCPPacket(true, false, clientSeq, 0);System.out.println("👤 客户端 → 服务端:" + syn);System.out.println("   含义:我想建立连接,我的初始序号是 " + clientSeq);// 第二次握手:服务端响应SYN+ACKTCPPacket synAck = new TCPPacket(true, true, serverSeq, clientSeq + 1);System.out.println("🔄 服务端 → 客户端:" + synAck);System.out.println("   含义:收到了,我同意连接,我的序号是 " + serverSeq + ",期待你的序号 " + (clientSeq + 1));// 第三次握手:客户端发送ACKTCPPacket ack = new TCPPacket(false, true, clientSeq + 1, serverSeq + 1);System.out.println("✅ 客户端 → 服务端:" + ack);System.out.println("   含义:收到确认,连接建立成功!");System.out.println("=" * 50);System.out.println("🎉 三次握手完成,开始数据传输!");}
}

面试热点:高频问题全解析

Q1: 为什么TCP需要三次握手?

标准答案:
确保双方的发送和接收能力都正常:

  • 第一次:确认客户端发送能力、服务端接收能力
  • 第二次:确认服务端发送能力、客户端接收能力
  • 第三次:确认客户端接收到服务端的确认

加分回答:
防止旧的重复连接请求突然又传送到服务端,避免产生错误连接。

Q2: 三次握手过程中丢包怎么办?

第一次握手丢包: 客户端超时重传SYN
第二次握手丢包: 客户端重传SYN,服务端重传SYN+ACK
第三次握手丢包: 服务端重传SYN+ACK,客户端重传ACK

Q3: 能否设计成两次握手?

不能! 两次握手无法确认客户端的接收能力,会导致:

  1. 服务端无法确认客户端是否收到连接确认
  2. 可能建立无效连接,浪费服务端资源
  3. 无法防范延迟连接请求的问题

Q4: SYN攻击的原理和防护?

攻击原理:

攻击者发送大量SYN请求 → 服务端维护大量半连接 → 
资源耗尽 → 无法处理正常请求

防护策略:

  • SYN Cookie: 不保存连接状态,通过算法验证
  • 超时机制: 快速清理无效连接
  • 连接限制: 限制单IP连接数

Q5: 握手过程中的序列号有什么作用?

核心作用:

  1. 防重放攻击: 每次连接使用不同的初始序号
  2. 保证顺序: 确保数据包按正确顺序组装
  3. 可靠传输: 配合确认号实现重传机制

实战应用:优化连接性能

连接池优化

// 使用连接池避免频繁握手
HikariConfig config = new HikariConfig();
config.setMaximumPoolSize(20);  // 维护20个连接
config.setMinimumIdle(5);       // 最少保持5个空闲连接
HikariDataSource dataSource = new HikariDataSource(config);// 这样就避免了每次数据库操作都要三次握手

Keep-Alive机制

// HTTP Keep-Alive复用TCP连接
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestProperty("Connection", "keep-alive");
// 一次握手,多次请求!

总结

三次握手虽然看起来简单,但它是网络通信可靠性的基石。掌握了三次握手,你就理解了:

🔹 为什么网络连接需要时间 - 握手需要时间
🔹 为什么有些攻击很危险 - SYN洪水攻击原理
🔹 为什么要使用连接池 - 避免频繁握手开销
🔹 为什么网络编程要考虑异常 - 握手可能失败

记住这个比喻,三次握手就像两个人见面前的确认过程,确保双方都准备好了再开始重要的交流。简单、有效、不可缺少!

下次面试官问起三次握手,你就可以从原理讲到应用,从安全讲到优化,展现你的深度思考能力! 🚀

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

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

相关文章

dubbo使用nacos作为注册中心配置

<dubbo:registry protocol"nacos" address"${dubbo.registry.address.nacos}" /> <dubbo:metadata-report address"${dubbo.metadata-report.address}"/> 如果有多个地址&#xff0c;这块如何配置呢&#xff1f; nacos://ip:端口?…

教师角色的转变:从知识传授者到学习引导者

教师角色的转变&#xff1a;从知识传授者到学习引导者 随着人工智能&#xff08;AI&#xff09;和信息技术的迅速发展&#xff0c;教育正在经历深刻的变革。其中&#xff0c;教师角色的转变尤为关键。传统上&#xff0c;教师主要承担“知识传授者”的职责&#xff0c;即向学生…

PostgreSQL 用户权限与安全管理

1 系统默认角色 postgres# select rolname from pg_roles; rolname ----------------------------- postgres pg_database_owner pg_read_all_data pg_write_all_data pg_monitor pg_read_all_settings pg_read_all_stats pg_stat_scan_tables …

C++构造函数和析构函数

C++构造函数和析构函数 C++的构造函数和析构函数是类的特殊成员函数,用于对象的创建和销毁,分别在对象的生命周期开始和结束时自动调用。它们的使用对资源管理和对象的初始化/清理至关重要。 1. 构造函数 定义 构造函数在对象创建时自动调用,用于初始化对象的数据成员。构造…

根据Cortex-M3(STM32F1)权威指南讲解MCU内存架构与如何查看编译器生成的地址具体位置

首先我们先查看官方对于Cortex-M3预定义的存储器映射 1.存储器映射 1.1 Cortex-M3架构的存储器结构 内部私有外设总线&#xff1a;即AHB总线&#xff0c;包括NVIC中断&#xff0c;ITM硬件调试&#xff0c;FPB, DWT。 外部私有外设总线&#xff1a;即APB总线&#xff0c;用于…

软件设计师“测试用例”考点分析——求三连

一、测试用例设计核心要点解析 1. 白盒测试覆盖标准 &#xff08;1&#xff09;路径覆盖&#xff1a;需覆盖程序中所有可能的路径。如2018年真题路径覆盖需要3组测试用例&#xff08;①②、①③、①③④&#xff09;&#xff0c;2020年流程图则需4个用例覆盖ace/abd/abe/acd四…

Linux 用户无法远程连接服务器

前言 昨天深夜一点多接到客户电话&#xff0c;客户说OS用户下午下班前还能正常登录。因为晚上一点半需要关闭所有服务进行迁移&#xff0c;但是用户无法登录了&#xff0c;导致后续流程无法执行。我让他先通过root用户紧急修改了密码&#xff0c;先保证业务正常流转。 问题 …

多模态大语言模型arxiv论文略读(八十八)

MammothModa: Multi-Modal Large Language Model ➡️ 论文标题&#xff1a;MammothModa: Multi-Modal Large Language Model ➡️ 论文作者&#xff1a;Qi She, Junwen Pan, Xin Wan, Rui Zhang, Dawei Lu, Kai Huang ➡️ 研究机构: ByteDance, Beijing, China ➡️ 问题背景…

svn迁移到git保留记录和Python字符串格式化 f-string的进化历程

svn迁移到git保留记录 and Python字符串格式化(二&#xff09;&#xff1a; f-string的进化历程 在将项目从SVN迁移到Git时&#xff0c;保留完整的版本历史记录非常重要。下面是详细的步骤和工具&#xff0c;可以帮助你完成这一过程&#xff1a; 安装Git和SVN工具 首先&#…

springboot配置mysql druid连接池,以及连接池参数解释

文章目录 前置配置方式参数解释 前置 springboot 项目javamysqldruid 连接池 配置方式 在 springboot 的 application.yml 中配置基本方式 # Druid 配置&#xff08;Spring Boot YAML 格式&#xff09; spring:datasource:url: jdbc:mysql://localhost:3306/testdb?useSSL…

vue实现高亮文字效果——advanced-mark.js

组件介绍-advanced-mark.js&#xff1a; advanced-mark.js 是一个用于 Vue 的高亮文字组件&#xff0c;它可以帮助你在文本中高亮显示指定的关键词或短语。 组件地址&#xff1a;https://angezid.github.io/advanced-mark.js/doc-v2/getting-started.html 主要功能&#xff1…

DC30V/2.5A同步降压芯片SL1581 输入24V降压5V 12V2A电流

在工业自动化、汽车电子等领域&#xff0c;24V 电源系统向 5V/12V 双轨供电的需求日益增长。针对这一痛点&#xff0c;森利威尔电子重磅推出 DC30V/2.5A 同步降压芯片 SL1581&#xff0c;凭借卓越的性能和创新设计&#xff0c;为工程师提供高可靠性、高性价比的电源解决方案。 …

React 第四十四节Router中 usefetcher的使用详解及注意事项

前言 useFetcher 是 React Router 中一个强大的钩子&#xff0c;用于在不触发页面导航的情况下执行数据加载&#xff08;GET&#xff09;或提交&#xff08;POST&#xff09;。 一、useFetcher 应用场景&#xff1a; 1、后台数据预加载&#xff08;如鼠标悬停时加载数据&…

Jmeter(三) - 测试计划(Test Plan)的元件

1.简介 上一篇已经教你如何通过JMeter来创建一个测试计划&#xff08;Test Plan&#xff09;&#xff0c;那么这一篇我们就将JMeter启动起来&#xff0c;创建一个测试计划&#xff08;Test plan&#xff09;&#xff0c;给大家介绍一下测试计划&#xff08;Test Plan&#xff…

应届本科生简历制作指南

一、找一个专业的简历模板 首先&#xff0c;你需要访问 Overleaf 的官方网站&#xff0c;也就是Overleaf, Online LaTeX Editor&#xff0c;进入页面后&#xff0c;点击注册按钮&#xff0c;按照提示填写相关信息来创建一个属于自己的账号&#xff0c;通常需要填写用户名、邮箱…

[Spring Boot]整合Java Mail实现Outlook发送邮件

日常开发过程中,我们经常需要使用到邮件发送任务,比方说验证码的发送、日常信息的通知等。日常比较常用的邮件发送方包括:163、QQ等,本文主要讲解Outlook SMTP的开启方式、OutLook STARTTTL的配置、如何通过JavaMail来实现电子邮件的发送等。 Outlook作为微软提供的企业电子…

【YOLOs-CPP-图像分类部署】03-解决报错

完整项目链接 点击here下载! 上一篇问题 经过上一篇博客,我们得到了一个粗略版(会报错)的项目。如何解决异常报错呢? 我把问题在github上对作者进行了提问,但是2天后,依然没有回复。 怎么办呢?只能自己调试代码了。 修改代码 经过大量调试,修改了YOLO11CLASS.h…

Dockers Compose常用指令介绍

Dockers Compose常用指令 1、常用指令介绍 1.1、version 指令 顶级一级指令&#xff0c;指定 compose 指定文件格式版本 version: "3.8" services: 不同版本支持的功能不同。常用版本有 ‘2’, ‘3’, ‘3.8’ 等。 1.2、services 指令 顶级一级指令&#xff0…

谢飞机的Spring WebFlux面试之旅:从基础到深入

谢飞机的Spring WebFlux面试之旅&#xff1a;从基础到深入 面试场景&#xff1a;谢飞机的WebFlux面试 面试官&#xff1a;你好&#xff0c;谢飞机&#xff0c;请介绍一下你自己。 谢飞机&#xff1a;您好&#xff0c;我是一名有三年开发经验的Java程序员&#xff0c;熟悉Spr…

Mysql增量备份与恢复

1.练习数据增量备份 增量备份&#xff1a;备份上次备份后&#xff0c;新产生的数据。 PERCONA Xtrabackup是一款强大的在线热备份工具&#xff0c;备份过程中不锁库表&#xff0c;适合生产环境。支持完全备份与恢复、增量备份与恢复、差异备份与恢复。 安装Xtrabackup 150、…