当前位置: 首页 > news >正文

go 模拟TCP粘包和拆包,及解决方法

1. 什么是 TCP 粘包与拆包?

  1. 粘包(Sticky Packet)
    粘包是指在发送多个小的数据包时,接收端会将这些数据包合并成一个数据包接收。由于 TCP 是面向流的协议,它并不会在每次数据发送时附加边界信息。所以当多个数据包按顺序发送时,接收端可能会一次性接收多个数据包的数据,造成数据被粘在一起。
    粘包一般发生在发送端每次写入的数据 < 接收端套接字(Socket)缓冲区的大小。

假设发送端发送了两个消息:消息1:“Hello”,消息2:“World”;由于 TCP 是流协议,接收端可能会接收到如下数据:“HelloWorld”。这种情况就是粘包,接收端就无法准确区分这两个消息。

  1. 拆包(Packet Fragmentation)
    拆包是指发送的数据包在传输过程中被分割成多个小包。尽管发送端可能发送了一个完整的消息,但由于 TCP 协议在网络传输时可能会对数据进行分段,接收端可能接收到的是多个小数据包。
    拆包一般发生在发送端每次写入的数据 > 接收端套接字(Socket)缓冲区的大小。

假设发送端发送了一个大的消息:“Hello, this is a long message.”;但是在传输过程中,网络层可能会将该消息拆分成多个小包,接收端可能先收到一部分数据:“Hello, this”,然后再收到另外一部分:“is a long message.”;这样接收端就会得到多个数据包,且它们并不代表单一的逻辑消息。

2. go 模拟TCP粘包

  1. server.go(接收端)
package mainimport ("bufio""fmt""io""net"
)func handleConnection(conn net.Conn) {defer conn.Close()// 创建缓冲读取器,读取客户端数据reader := bufio.NewReader(conn)var buffer [1024]bytefor {// 持续读取数据n, err := reader.Read(buffer[:])if err == io.EOF {break}if err != nil {fmt.Println("Error reading data:", err)break}recvStr := string(buffer[:n])// 打印接收到的数据fmt.Println("Received:", recvStr)}
}func main() {// 启动服务器,监听 8080 端口ln, err := net.Listen("tcp", ":8080")if err != nil {fmt.Println("Error starting server:", err)return}defer ln.Close()fmt.Println("Server started on port 8080...")for {// 等待客户端连接conn, err := ln.Accept()if err != nil {fmt.Println("Error accepting connection:", err)continue}// 处理连接go handleConnection(conn)}
}
  1. client.go(发送端)
package mainimport ("fmt""net""time"
)func main() {// 连接到服务器conn, err := net.Dial("tcp", "localhost:8080")if err != nil {fmt.Println("Error connecting to server:", err)return}defer conn.Close()// 模拟粘包和拆包for i := 0; i < 100; i++ {// 发送粘包情况:多个小消息一次发送message := fmt.Sprintf("Message %d\n", i+1)conn.Write([]byte(message))}// 等待服务器输出接收到的消息time.Sleep(2 * time.Second)
}
  1. 执行结果分析

在这里插入图片描述

可以看到接收端收到的消息并非都是一条,说明发生了粘包

3. go模拟TCP拆包

  1. server.go(接收端)
package mainimport ("bufio""fmt""io""net"
)func handleConnection(conn net.Conn) {defer conn.Close()// 创建缓冲读取器,读取客户端数据reader := bufio.NewReader(conn)var buffer [18]bytefor {// 持续读取数据n, err := reader.Read(buffer[:])if err == io.EOF {break}if err != nil {fmt.Println("Error reading data:", err)break}recvStr := string(buffer[:n])// 打印接收到的数据fmt.Println("Received message :", recvStr)}
}func main() {// 启动服务器,监听 8080 端口ln, err := net.Listen("tcp", ":8080")if err != nil {fmt.Println("Error starting server:", err)return}defer ln.Close()fmt.Println("Server started on port 8080...")for {// 等待客户端连接conn, err := ln.Accept()if err != nil {fmt.Println("Error accepting connection:", err)continue}// 处理连接go handleConnection(conn)}
}
  1. client.go(发送端)
package mainimport ("fmt""net""strings""time"
)func main() {// 连接到服务器conn, err := net.Dial("tcp", "localhost:8080")if err != nil {fmt.Println("Error connecting to server:", err)return}defer conn.Close()// 构造一个超过默认 MTU 的大数据包(32 字节)message := strings.Repeat("A", 32)// 模拟发送大量数据for i := 0; i < 100; i++ {fmt.Printf("Sending message : %s\n", message)conn.Write([]byte(message))}// 等待服务器输出time.Sleep(2 * time.Second)
}
  1. 执行结果分析
    在这里插入图片描述

可以看到接收端对接收到的数据进行了拆分,说明发生了拆包

4. 如何解决 TCP 粘包与拆包问题?

4.1 自定义协议

发送端将请求的数据封装为两部分:消息头(发送数据大小)+消息体(发送具体数据);接收端根据消息头的值读取相应长度的消息体数据

  1. server.go(接收端)
    服务端接收到数据时,首先读取前4个字节来获取消息的长度,然后再根据该长度读取完整的消息体
package mainimport ("encoding/binary""fmt""io""log""net"
)// readMessage 函数根据长度字段读取消息
func readMessage(conn net.Conn) (string, error) {// 读取4个字节的长度字段lenBytes := make([]byte, 4)_, err := io.ReadFull(conn, lenBytes)if err != nil {return "", fmt.Errorf("failed to read length field: %v", err)}// 解析消息长度msgLength := binary.BigEndian.Uint32(lenBytes)// 读取消息体msgBytes := make([]byte, msgLength)_, err = io.ReadFull(conn, msgBytes)if err != nil {return "", fmt.Errorf("failed to read message body: %v", err)}return string(msgBytes), nil
}func handleConnection(conn net.Conn) {defer conn.Close()// 一直循环接收客户端发来的消息for {msg, err := readMessage(conn)if err != nil {log.Printf("Error reading message: %v", err)break}fmt.Println("Received message:", msg)}
}func main() {// 启动监听服务listener, err := net.Listen("tcp", ":8080")if err != nil {log.Fatalf("Error starting server: %v", err)}defer listener.Close()fmt.Println("Server is listening on port 8080...")// 接受客户端连接并处理for {conn, err := listener.Accept()if err != nil {log.Printf("Error accepting connection: %v", err)continue}// 启动新的 Goroutine 处理客户端请求go handleConnection(conn)}
}
  1. client.go(发送端)
    客户端将连接到服务端,并发送多个消息。每个消息的前4字节表示消息的长度,随后是消息体
package mainimport ("bytes""encoding/binary""log""net"
)// sendMessage 函数将消息和长度一起发送给服务端
func sendMessage(conn net.Conn, msg string) {// 计算消息的长度msgLen := uint32(len(msg))buf := new(bytes.Buffer)// 将消息长度转换为4字节的二进制数据binary.Write(buf, binary.BigEndian, msgLen)// 将消息体内容添加到缓冲区buf.Write([]byte(msg))// 发送缓冲区数据到服务端conn.Write(buf.Bytes())
}func main() {// 连接到服务端conn, err := net.Dial("tcp", "127.0.0.1:8080")if err != nil {log.Fatalf("Error connecting to server: %v", err)}defer conn.Close()// 发送多个消息sendMessage(conn, "Hello, Server!")sendMessage(conn, "This is a second message.")sendMessage(conn, "Goodbye!")
}
4.2 固定长度数据包

每个消息的长度是固定的(例如 1024 字节)。如果客户端发送的数据长度不足指定长度,则会使用空格填充,确保每个数据包的大小一致

  1. server.go(接收端)
    服务端接收到的数据是固定长度的。每次接收 1024 字节的数据,并将其打印出来。如果数据不足 1024 字节,服务端会读取并处理这些数据。
package mainimport ("fmt""io""log""net""strings"
)// handleConnection 函数处理每个客户端的连接
func handleConnection(conn net.Conn) {defer conn.Close()// 设定每个消息的固定长度const messageLength = 1024buf := make([]byte, messageLength)for {// 每次读取固定长度的消息_, err := io.ReadFull(conn, buf)if err != nil {if err.Error() == "EOF" {// 客户端关闭连接break}log.Printf("Error reading message: %v", err)break}// 将读取的字节转换为字符串并打印msg := string(buf)// 去除空格填充fmt.Println("Received message:", strings.TrimSpace(msg))}
}func main() {// 启动 TCP 监听listener, err := net.Listen("tcp", ":8080")if err != nil {log.Fatalf("Error starting server: %v", err)}defer listener.Close()fmt.Println("Server is listening on port 8080...")// 等待客户端连接for {conn, err := listener.Accept()if err != nil {log.Printf("Error accepting connection: %v", err)continue}// 启动新的 Goroutine 处理每个客户端的连接go handleConnection(conn)}
}
  1. client.go(发送端)
    客户端会向服务器发送固定长度的消息,如果消息长度不足 1024 字节,则会填充空格
package mainimport ("log""net""strings"
)// sendFixedLengthMessage 函数向服务端发送固定长度的消息
func sendFixedLengthMessage(conn net.Conn, msg string) {// 确保消息长度为 1024 字节,不足部分用空格填充if len(msg) < 1024 {msg = msg + strings.Repeat(" ", 1024-len(msg))}// 发送消息到服务端_, err := conn.Write([]byte(msg))if err != nil {log.Fatalf("Error sending message: %v", err)}
}func main() {// 连接到服务端conn, err := net.Dial("tcp", "127.0.0.1:8080")if err != nil {log.Fatalf("Error connecting to server: %v", err)}defer conn.Close()// 发送固定长度的消息sendFixedLengthMessage(conn, "Hello, Server!")sendFixedLengthMessage(conn, "This is a second message.")sendFixedLengthMessage(conn, "Goodbye!")
}
4.3 特殊字符来标识消息边界

通过在发送端每条消息的末尾加上 \n,然后接收端使用 ReadLine() 方法按行读取数据来区分每个数据包的边界

  1. server.go(接收端)
    服务端会监听端口,并按行读取客户端发送的消息。每个消息的末尾会有一个 \n 来标识消息的结束
package mainimport ("bufio""fmt""log""net""strings"
)func handleConnection(conn net.Conn) {defer conn.Close()// 创建一个带缓冲的读取器reader := bufio.NewReader(conn)for {// 读取客户端发送的一行数据,直到遇到 '\n' 为止line, err := reader.ReadString('\n')if err != nil {log.Printf("Error reading from client: %v", err)break}// 去掉结尾的换行符line = strings.TrimSpace(line)fmt.Printf("Received message: %s\n", line)}
}func main() {// 启动 TCP 监听listener, err := net.Listen("tcp", ":8080")if err != nil {log.Fatalf("Error starting server: %v", err)}defer listener.Close()fmt.Println("Server is listening on port 8080...")// 等待客户端连接for {conn, err := listener.Accept()if err != nil {log.Printf("Error accepting connection: %v", err)continue}// 启动新的 Goroutine 处理每个客户端的连接go handleConnection(conn)}
}
  1. client.go(发送端)
    客户端向服务端发送消息,每条消息末尾都会加上一个 \n,然后发送到服务器
package mainimport ("log""net"
)func sendMessage(conn net.Conn, message string) {// 将消息添加换行符并发送message = message + "\n"_, err := conn.Write([]byte(message))if err != nil {log.Fatalf("Error sending message: %v", err)}
}func main() {// 连接到服务端conn, err := net.Dial("tcp", "127.0.0.1:8080")if err != nil {log.Fatalf("Error connecting to server: %v", err)}defer conn.Close()// 发送几条消息sendMessage(conn, "Hello, Server!")sendMessage(conn, "How are you?")sendMessage(conn, "Goodbye!")
}

5. 三种方式的优缺点对比

特性固定长度方式特殊字符分隔方式自定义协议方式
实现简单
带宽效率低(需要填充)高(仅传输有效数据)高(仅传输有效数据,且灵活处理)
灵活性
易于调试高(每包大小固定)中(需解析换行符等)低(需要解析协议头和体)
性能开销中等(需要额外解析消息头)
适用场景长度固定的消息消息大小可变但有清晰的分隔符复杂协议、支持多类型消息的场景

相关文章:

go 模拟TCP粘包和拆包,及解决方法

1. 什么是 TCP 粘包与拆包&#xff1f; 粘包&#xff08;Sticky Packet&#xff09; 粘包是指在发送多个小的数据包时&#xff0c;接收端会将这些数据包合并成一个数据包接收。由于 TCP 是面向流的协议&#xff0c;它并不会在每次数据发送时附加边界信息。所以当多个数据包按顺…...

论文笔记PhotoReg: Photometrically Registering 3D Gaussian Splatting Models

1.abstract 最近推出的3D高斯飞溅(3DGS)&#xff0c;它用多达数百万个原始椭球体来描述场景&#xff0c;可以实时渲染。3DGS迅速声名鹊起。然而&#xff0c;一个关键的悬而未决的问题仍然存在&#xff1a;我们如何将多个3DG融合到一个连贯的模型中&#xff1f;解决这个问题将使…...

宝塔服务器安装备份配置

1.服务器下载安装宝塔功能 if [ -f /usr/bin/curl ];then curl -sSO https://download.bt.cn/install/install_panel.sh;else wget -O install_panel.sh https://download.bt.cn/install/install_panel.sh;fi;bash install_panel.sh ed8484bec执行后选择y 等待下载完成会给出…...

ubuntu22 安装CUDA

在Ubuntu系统中&#xff0c;使用nvidia-smi命令可以看到当前GPU信息&#xff0c;在右上角可以看到CUDA Version&#xff0c;意思是最大支持的CUDA版本号。 安装下载 CUDA Toolkit 11.6 Downloads | NVIDIA Developer https://developer.nvidia.com/cuda-downloads?target_osL…...

LabVIEW故障诊断中的无故障数据怎么办

在使用LabVIEW进行故障诊断时&#xff0c;可能会面临“无故障数据”的情况。这种情况下&#xff0c;缺乏明确的故障参考&#xff0c;使得系统难以通过传统对比法进行故障识别。本文将介绍应对无故障数据的关键策略&#xff0c;包括数据模拟、特征提取和基于机器学习的方法&…...

开发模式选择与最佳实践指南20241230

开发模式选择与最佳实践指南 引言 在现代软件开发中&#xff0c;选择合适的开发模式直接影响项目的开发效率和质量。本文将帮助您&#xff1a; &#x1f3af; 了解三种主流开发模式的优缺点&#x1f4a1; 根据项目特点选择最适合的开发模式&#x1f527; 掌握混合开发模式的…...

超详细!一文搞定PID!嵌入式STM32-PID位置环和速度环

本文目录 一、知识点1. PID是什么&#xff1f;2. 积分限幅--用于限制无限累加的积分项3. 输出值限幅--用于任何pid的输出4. PID工程 二、各类PID1. 位置式PID&#xff08;用于位置环&#xff09;&#xff08;1&#xff09;公式&#xff08;2&#xff09;代码使用代码 2. 增量式…...

Redhat7 PCS建立无共享存储浮动地址集群

更新记录 日期版本号内容9/22/2024Ver 1.0重新排版修正 0写在前面 0.1 简述 时间有限使用VMware6.7环境使用Centos7.8最小化安装方式&#xff08;不用配置本地yum仓库&#xff09;注意查看主机名&#xff08;主机双机操作&#xff0c;部分单机操作&#xff09; 序号HostIPAd…...

爱思唯尔word模板

爱思唯尔word模板 有时候并不一定非得latex https://download.csdn.net/download/qq_38998213/90199214 参考文献书签链接...

交换机Vlan中 tagged和untagged的区别

pvid&#xff0c;tagged与untagged pvid是交换机一个端口上的id&#xff0c;一个端口只能有一个pvid&#xff0c;多个端口可以有相同的pvid。 一&#xff1a;接收数据 Untagged&#xff1a;不管收到的数据帧是否已经有VLAN标记&#xff0c;将数据帧中的vlan标记修改为自己的pvi…...

软件需求分析期末知识点整理

前言&#xff1a;本文为wk学子量身打造&#xff0c;帮助大家少挂科。主要根据ls的会议进行整理。懂得都懂。 重点还是多看看课本 第2章 需求获取的方法 第3章 3.1.2 控制需求(案例*2) 第4章 4.3 范式 第5章 5.2.3 原子功能(案例) 5.2.4 划分功能(案例)5.3.3 工作流图(画图) 第…...

PyAudio使用手册

PyAudio 是一个功能强大的 Python 库&#xff0c;用于在 Python 中进行音频输入和输出操作 1. 安装 在使用 PyAudio 之前&#xff0c;需要先安装它。可以使用 pip 进行安装&#xff1a; pip install pyaudio在某些系统&#xff08;如 Ubuntu&#xff09;上&#xff0c;可能还需…...

总结TCP/IP四层模型

总结TCP/IP四层模型 阅读目录(Content) 一、TCP/IP参考模型概述 1.1、TCP/IP参考模型的层次结构二、TCP/IP四层功能概述 2.1、主机到网络层  2.2、网络互连层  2.3、传输层  2.3、应用层 三、TCP/IP报文格式 3.1、IP报文格式3.2、TCP数据段格式3.3、UDP数据段格式3.4、套…...

《深入挖掘Python加解密:自定义加密算法的设计与实现》

利用python实现加解密 在正式编写各种加解密前&#xff0c;我们先写个小案例&#xff0c;如下。 封面在文末呦&#xff01; 基础加解密-源码 # 加密 def encode():source01 乐茵for c in source01:ascii01 ord(c)ascii01 1print(chr(ascii01), end)# 解密 def decode():…...

【前端,TypeScript】TypeScript速成(六):函数

函数 函数的定义 定义一个最简单的加法函数&#xff1a; function add(a: number, b: number): number {return a b }&#xff08;可以看到 JavaScript/TypeScript 的语法与 Golang 也非常的相似&#xff09; 调用该函数&#xff1a; console.log(add(2, 3)) // out [LOG…...

Python中元组(tuple)内置的数据类型

在Python中&#xff0c;元组&#xff08;tuple&#xff09;是一种内置的数据类型&#xff0c;用于存储不可变的有序元素集合。元组在很多方面与列表&#xff08;list&#xff09;相似&#xff0c;但它们之间存在一些关键的区别。以下是关于Python元组的详细解释&#xff1a; 定…...

AI安全的挑战:如何让人工智能变得更加可信

引言 随着人工智能&#xff08;AI&#xff09;技术在各个领域的广泛应用&#xff0c;尤其是在医疗、金融、自动驾驶和智能制造等行业&#xff0c;AI正在重塑我们的工作和生活方式。从提高生产效率到实现个性化服务&#xff0c;AI带来了前所未有的便利。然而&#xff0c;在享受这…...

redis用途都有哪些

Redis&#xff0c;作为一个开源的高性能键值对数据库&#xff0c;其用途广泛且功能强大。 1. 缓存&#xff08;Caching&#xff09;&#xff1a; • Redis常被用作缓存层&#xff0c;存储那些频繁访问但不易改变的数据&#xff0c;如用户会话、商品详情等。 • 通过将这些数据存…...

【Django篇】--动手实现路由模块化与路由反转

一、路由模块化 在一个Django项目中&#xff0c;由于功能类别不同&#xff0c;因此需要将不同功能进行模块化设计。在Django项目中模块化设计则需要将不同模块封装为对应的app模块&#xff0c;每一个模块中涉及到的路由则也需要进行模块化设计&#xff0c;才能更好的让整个项目…...

自研国产零依赖前端UI框架实战008 用户表单以及随机ID

前言 通过前面的努力,我们的组件已经越来越多了,我们的功能也越来越完善. 不过我们的新增用户的功能还没有做. 接下来, 就让我们实现新增用户的功能. 显示新增用户的表单 首先, 我们先把新增用户的表单显示出来. 我们可以复用之前的组件. <zdp_button1 text"新增…...

【数据结构-单调队列】力扣LCR 184. 设计自助结算系统

请设计一个自助结账系统&#xff0c;该系统需要通过一个队列来模拟顾客通过购物车的结算过程&#xff0c;需要实现的功能有&#xff1a; get_max()&#xff1a;获取结算商品中的最高价格&#xff0c;如果队列为空&#xff0c;则返回 -1 add(value)&#xff1a;将价格为 value …...

项目管理和协作平台Maintainer、Guest、Reporter、Owner 和 Developer 是常见的用户角色

在项目管理和协作平台上&#xff0c;Maintainer、Guest、Reporter、Owner 和 Developer 是常见的用户角色&#xff0c;每个角色有不同的权限和责任。以下是这些角色的详细区别&#xff1a; 1. Guest&#xff08;访客&#xff09; 权限&#xff1a;最低级别的权限。访问&#…...

探索电商数据:爬取不同平台商品信息的Python实践

在数字化时代&#xff0c;电商平台的商品信息成为了宝贵的数据资源。除了亚马逊&#xff0c;全球还有许多电商平台的商品信息值得爬取。本文将介绍几个值得关注的电商平台&#xff0c;并提供Python代码示例&#xff0c;展示如何爬取这些平台的商品信息。 1. 京东 (JD.com) 京…...

Autoware Universe 安装记录

前提&#xff1a; ubuntu20.04&#xff0c;英伟达显卡。 演示&#xff1a;https://www.bilibili.com/video/BV1z4CbYFEwr/?spm_id_from333.337.search-card.all.click ROS2-Galactic安装 wget http://fishros.com/install -O fishros && . fishros 选择galactic(R…...

CAT3D: Create Anything in 3D with Multi-View Diffusion Models 论文解读

24年5月的论文&#xff0c;上一版就是ReconFusion 目录 一、概述 二、相关工作 1、2D先验 2、相机条件下的2D先验 3、多视角先验 4、视频先验 5、前馈方法 三、Method 1、多视角扩散模型 2、新视角生成 3、3D重建 一、概述 该论文提出一种CAT3D方法&#xff0c;实现…...

群落生态学研究进展▌Hmsc包对于群落生态学假说的解读、Hmsc包开展单物种和多物种分析的技术细节及Hmsc包的实际应用

HMSC&#xff08;Hierarchical Species Distribution Models&#xff09;是一种用于预测物种分布的统计模型。它在群落生态学中的应用广泛&#xff0c;可以帮助科学家研究物种在不同环境条件下的分布规律&#xff0c;以及预测物种在未来环境变化下的潜在分布范围。 举例来说&a…...

C 进阶 — 程序环境和预处理

C 进阶 — 程序环境和预处理 主要内容 程序的编译和执行环境 C 程序编译和链接 预定义符号 预处理指令 #define 预处理指令 #include 预处理指令 #undef 预处理操作符 # 和 ## 宏和函数对比 命令行定义 条件编译 一 程序的编译和执行环境 ANSI C 存在两个不同环境…...

基于单片机的温湿度采集系统(论文+源码)

2.1系统的功能 本系统的研制主要包括以下几项功能&#xff1a; (1)温度检测功能&#xff1a;对所处环境的温度进行检测&#xff1b; (2)湿度检测功能&#xff1a;对所处环境的湿度进行检测&#xff1b; (3)加热和制冷功能&#xff1a;可以完成加热和制冷功能。 (4)加湿和除…...

【数据分析处理之缺失值】

文章目录 一、缺失值的影响1. 统计分析的偏差2. 机器学习模型的性能下降3. 数据质量和可信度下降4. 数据利用率降低5. 增加数据预处理的复杂度 二、识别缺失值1. 使用工具识别缺失值2. 可视化缺失数据 三、处理缺失值的策略1. 删除含缺失值的行或列2. 填充缺失值a. 用常数填充b…...

【大模型实战篇】Mac本地部署RAGFlow的踩坑史

1. 题外话 最近一篇文章还是在11月30日写的&#xff0c;好长时间没有打卡了。最近工作上的事情特别多&#xff0c;主要聚焦在大模型的预训练、微调和RAG两个方面。主要用到的框架是Megatron-DeepSpeed&#xff0c;后续会带来一些分享。今天的文章主要聚焦在RAG。 近期调研了一系…...

SQL Server实现将分组的其他字段数据拼接成一条数据

在 SQL Server 中&#xff0c;可以使用 STRING_AGG 函数&#xff08;SQL Server 2017 及更高版本支持&#xff09;将分组的其他字段数据拼接成一条数据。以下是示例代码&#xff1a; 假设有一个表 Orders&#xff0c;结构如下&#xff1a; OrderIDCustomerIDProduct1C001Appl…...

STM32 高级 物联网通讯之蓝牙通讯

目录 蓝牙基础知识 蓝牙概述 蓝牙产生背景 蓝牙发展历程 蓝牙技术类型 经典蓝牙(BR/EDR和AMP) 低功耗蓝牙(BLE) 市场上常见蓝牙架构 SOC蓝牙单芯片方案 SOC蓝牙+MCU方案 蓝牙host+controller分开方案 蓝牙协议栈 蓝牙芯片架构 BLE低功耗蓝牙协议栈框架 物理…...

堆排序基础与实践:如何在Java中实现堆排序

目录 一、堆排序的基本原理 二、堆排序的实现步骤 三、堆排序的时间复杂度和空间复杂度 四、堆排序的工作流程 五、堆排序的优缺点 六、堆排序的应用场景 堆排序&#xff08;Heap Sort&#xff09;是一种基于堆数据结构的排序算法。堆是一种特殊的完全二叉树&#xff0c;…...

你有哪些Deep Learning(RNN、CNN)调参的经验?

在深度学习的实践中&#xff0c;调参是一项既艺术又科学的工作。它不仅需要理论知识的支撑&#xff0c;还需要大量的实践经验。以下是一些在RNN和CNN模型调参中积累的经验&#xff0c;希望对正在这个领域摸索的朋友们有所帮助。 1. 从成熟的开源项目开始 对于初学者来说&…...

小程序租赁系统开发的优势与应用探索

内容概要 在如今这个数码科技飞速发展的时代&#xff0c;小程序租赁系统开发仿佛是一张神奇的魔法卡&#xff0c;能让租赁体验变得顺畅如丝。想象一下&#xff0c;无论你需要租用什么&#xff0c;从单车到房屋&#xff0c;甚至是派对用品&#xff0c;只需动动手指&#xff0c;…...

Spring Boot教程之三十九: 使用 Maven 将 Spring Boot 应用程序 Docker 化

如何使用 Maven 将 Spring Boot 应用程序 Docker 化&#xff1f; Docker是一个开源容器化工具&#xff0c;用于在隔离环境中构建、运行和管理应用程序。它方便开发人员捆绑其软件、库和配置文件。Docker 有助于将一个容器与另一个容器隔离。在本文中&#xff0c;为了将Spring B…...

Day58 图论part08

拓扑排序精讲 拓扑排序看上去很复杂,其实了解其原理之后,代码不难 代码随想录 import java.util.*;public class Main{public static void main (String[] args) {Scanner sc = new Scanner(System.in);int n = sc.nextInt();int m = sc.nextInt();List<List<Integer&…...

u3d中JSON数据处理

一.认识JSON 1.1 Json概述 JSON&#xff08;JavaScript Object Notation&#xff0c;JavaScript对象表示法&#xff09;JSON和XML是比较类似的技术&#xff0c;都是用来存储文本信息数据的&#xff1b;相对而言&#xff0c;JSON比XML体积更小巧&#xff0c;但是易读性不如XML…...

大语言模型(LLM)一般训练过程

大语言模型(LLM)一般训练过程 数据收集与预处理 收集:从多种来源收集海量文本数据,如互联网的新闻文章、博客、论坛,以及书籍、学术论文、社交媒体等,以涵盖丰富的语言表达和知识领域。例如,训练一个通用型的LLM时,可能会收集数十亿甚至上百亿字的文本数据.清洗:去除…...

第十六届蓝桥杯模拟赛(第一期)(C语言)

判断质因数 如果一个数p是个质数&#xff0c;同时又是整数a的约数&#xff0c;则p称为a的一个质因数。 请问2024有多少个质因数。 了解 约数&#xff0c;又称因数。整数a整除整数b&#xff0c;b为a的因数&#xff08;约数&#xff09;质数&#xff0c;又称素数。只有1和它本身两…...

某网站手势验证码识别深入浅出(全流程)

注意,本文只提供学习的思路,严禁违反法律以及破坏信息系统等行为,本文只提供思路 如有侵犯,请联系作者下架 本文识别已同步上线至OCR识别网站: http://yxlocr.nat300.top/ocr/other/20 本篇文章包含经验和教训总结,我采用了两种方法进行识别,两种方法都各有优劣,其中一…...

QT---------QT框架功能概述

常用Qt界面组件 Qt提供了丰富的界面组件&#xff0c;如QPushButton&#xff08;按钮&#xff09;、QLineEdit&#xff08;单行文本框&#xff09;、QTextEdit&#xff08;多行文本框&#xff09;、QLabel&#xff08;标签&#xff09;、QComboBox&#xff08;下拉框&#xff0…...

C++ 设计模式:模板方法(Template Method)

链接&#xff1a;C 设计模式 链接&#xff1a;C 设计模式 - 策略模式 链接&#xff1a;C 设计模式 - 观察者模式 模板方法&#xff08;Template Method&#xff09;是一种行为设计模式&#xff0c;它定义了一个操作中的算法的骨架&#xff0c;而将一些步骤延迟到子类中。通过这…...

下载mysql免安装版和配置

1、下载地址 点击去官网下载https://downloads.mysql.com/archives/community/ 2、解压安装mysql 解压的文件夹是没有my.ini文件和data目录&#xff0c;需要我们自己去创建 根目录下创建my.ini&#xff0c;根目录创建data [mysql] default-character-setutf8[mysqld] #端口 po…...

Web服务端技术原理及应用

前言 黄色的是考点&#xff0c;蓝色的是重点。 HTML/CSS/JS 本章会有一个7分的程序设计题&#xff0c;用到前端知识 form表单元素&#xff0c;常用表单元素 html:HTML快速上手 基础语法、css常用选择器&#xff08;ID、类&#xff09;、盒子模型 css:网页美化指南 JS …...

数据库的使用09:使用SSMS工具将SQLsever数据导出到Excel

第一步&#xff0c;新建一个空白的.csv文件 第二步&#xff0c;按步骤点击导出 第三步&#xff0c;选择数据源&#xff08;Db数据库&#xff09; 第四步&#xff0c;选择目标源&#xff08;CSV平面文件目标&#xff09; 第五步&#xff0c;指定表或SQL 一直点下一步即可&am…...

Python中__getitem__ 魔法方法

在Python中&#xff0c;__getitem__ 是一个特殊的方法&#xff0c;通常称为“魔法方法”或“双下方法”&#xff08;因为它们的名字前后都有两个下划线&#xff09;。__getitem__ 方法允许一个对象实现像序列&#xff08;如列表、元组、字符串&#xff09;一样的行为&#xff0…...

自动驾驶三维重建

大概八成估计是未来的科研方向了 Neural Radiance Field in Autonomous Driving: A Survey...

小程序中引入echarts(保姆级教程)

hello hello~ &#xff0c;这里是 code袁~&#x1f496;&#x1f496; &#xff0c;欢迎大家点赞&#x1f973;&#x1f973;关注&#x1f4a5;&#x1f4a5;收藏&#x1f339;&#x1f339;&#x1f339; &#x1f981;作者简介&#xff1a;一名喜欢分享和记录学习的在校大学生…...

INNER JOIN,LEFT JOIN,RIGHT JOIN,FULL JOIN这四个怎么在gorm中使用

在 GORM 中&#xff0c;JOIN 操作是通过 Joins 方法实现的&#xff0c;而不同类型的 JOIN&#xff08;如 INNER JOIN、LEFT JOIN、RIGHT JOIN 和 FULL JOIN&#xff09;可以通过特定的 SQL 语法来表示。GORM 本身并没有直接的 INNER, LEFT, RIGHT 等专用方法&#xff0c;但可以…...