~~~
package main
import (
"bytes"
"encoding/binary"
"fmt"
"github.com/klauspost/compress/zstd"
"log"
)
var (
/*
但凡是涉及到多字节的数值(整数、浮点数等)时,通常都需要明确字节序(如 uint16、uint32、int64、float64 等)
数值在二进制存储和传输时确实需要指定字节序(大端或小端),因为不同的系统可能采用不同的字节序
大端字节序:高位字节放在前面,低位字节放在后面
小端字节序:低位字节放在前面,高位字节放在后面
*/
byteOrder = binary.BigEndian
//全局缓冲区
buffer []byte
maxBufferSize = 10 * 1024 * 1024 // 增加 buffer 的最大大小限制,防止内存泄露
)
// 压缩数据
func ZSTDEncoder(data []byte) []byte {
var buf bytes.Buffer
encoder, err := zstd.NewWriter(&buf)
if err != nil {
return nil
}
defer encoder.Close()
_, err = encoder.Write(data)
if err != nil {
return nil
}
err = encoder.Close()
if err != nil {
return nil
}
return buf.Bytes()
}
// 解压数据
func ZSTDDecoder(data []byte) ([]byte, error) {
decoder, err := zstd.NewReader(nil)
if err != nil {
return nil, err
}
defer decoder.Close()
decompressedData, err := decoder.DecodeAll(data, nil)
if err != nil {
return nil, err
}
return decompressedData, nil
}
// 创建数据包(4字节数据长度 + 数据 + 1字节结束符OxFF)
func createPacket(data []byte) []byte {
var buf bytes.Buffer
// 压缩数据
compressedData := ZSTDEncoder(data)
//4字节数据长度
length := uint32(len(compressedData))
err := binary.Write(&buf, byteOrder, length)
if err != nil {
log.Fatal(err)
}
// 数据(压缩后的数据)
buf.Write(compressedData)
// 1字节结束符OxFF
buf.WriteByte(0xFF)
return buf.Bytes()
}
// 解析数据包
func sendPacket(packet []byte) {
// 追加数据到全局缓冲区
if packet != nil {
buffer = append(buffer, packet...)
if len(buffer) > maxBufferSize {
fmt.Println("buffer长度溢出, 清除缓冲区")
buffer = nil
return
}
}
// 解析数据包
for {
// 需要至少 5 字节数据(4 字节长度 + 1 字节结束符)
if len(buffer) < 5 {
break
}
// 读取长度字段(4 字节)
var length uint32
err := binary.Read(bytes.NewReader(buffer[:4]), byteOrder, &length)
if err != nil {
break // 数据不足,等待更多数据
}
// 计算完整数据包的长度
expectedLength := 4 + int(length) + 1
if len(buffer) < expectedLength {
break // 数据不足,等待更多数据
}
// 读取数据部分
compressedData := buffer[4 : 4+length]
// 检查结束符(0xFF)
if buffer[expectedLength-1] != 0xFF {
fmt.Println("错误的结束符, 丢弃损坏的数据包")
// **从当前 buffer 中查找下一个可能的正确数据包**
found := false
for i := 1; i < len(buffer)-4; i++ {
if buffer[i] == 0xFF {
buffer = buffer[i+1:] // 丢弃错误数据,继续解析
found = true
break
}
}
if !found {
buffer = nil // 没有找到新的起点,清空 buffer
}
continue
}
// 解压数据
data, err := ZSTDDecoder(compressedData)
if err != nil {
fmt.Println("Error: Failed to decompress data, discarding corrupted packet")
buffer = buffer[expectedLength:] // 丢弃错误数据
continue
}
// 打印解析结果
fmt.Println("Parsed Data:", string(data))
// 移除已解析的数据
buffer = buffer[expectedLength:]
}
}
func main() {
// 原始数据
data := []byte("Hello, World!")
packet := createPacket(data)
fmt.Println("----------------------模拟半包-------------------------")
packet1 := packet[0:3]
packet2 := packet[3:8]
packet3 := packet[8:]
sendPacket(packet1)
sendPacket(packet2)
sendPacket(packet3)
fmt.Println("----------------------模拟粘包-------------------------")
combinedPacket := append(packet, packet...)
combinedPacket = append(combinedPacket, packet...)
sendPacket(combinedPacket)
fmt.Println("----------------------模拟数据损坏或丢包-------------------------")
badPacket := append(createPacket([]byte("Corrupt Data")), 0x00) // 添加错误的结尾
sendPacket(badPacket)
sendPacket(packet)
sendPacket(packet)
sendPacket(packet)
}
//RSA算法(非对称加密算法)
//全局 buffer 并发不安全,需要 sync.Mutex 或 channel 解决竞争问题
//1.对压缩数据中的 0xFF 进行转义 给出对于的代码 或这个改成数据校验
//2.问题:频繁的 append 和切片操作可能引发内存拷贝。改进:使用 bytes.Buffer 或预分配缓冲区 给出对应的代码
//如果你的数据量较大,建议优化 并发处理,比如:
//生产者-消费者模型(goroutine + channel)
//去掉 0xFF 结束符,直接用 [4字节长度] + 数据 解析
//[2字节协议版本][2字节消息类型][4字节数据长度][数据][2字节校验和][1字节结束符(0xFF)]
//问题:数据在传输中可能损坏(如长度字段被篡改)。
//改进:在协议头或尾部加入校验字段(如 CRC32)
//问题:频繁的 append 和切片操作可能引发内存拷贝。
//改进:使用 bytes.Buffer 或预分配缓冲区
/*
开始符(Start byte):
0xAA 作为开始符,表示数据包的开始,确保不会误解析。
数据长度(Length):
使用 4 字节存储数据(压缩数据)的长度。由于采用了大端字节序,跨平台时数据可以正确解析。
序列号(Sequence Number):
用 4 字节存储,每个数据包都带有一个递增的序列号,用来保证包的顺序性,防止乱序或重放攻击。
时间戳(Timestamp):
使用 8 字节存储 Unix 时间戳,表示数据包创建的时间,可以防止重放攻击(例如,如果数据包的时间戳超出一定范围,则认为是篡改的)。
消息认证码(MAC):
使用 HMAC-SHA256 对数据(包括数据长度、压缩数据、序列号、时间戳等)进行签名,确保数据包未被篡改。
HMAC 密钥由发送方与接收方共享。
压缩数据(Compressed Data):
将数据压缩后存储,减少数据传输大小。
结束符(End byte):
0xFF 作为结束符,确保数据包的结束。
*/
//白名单
~~~
- 目录
- 第一例 gRPC使用例子
- 第二例 基于go-micro做服务注册和服务发现
- 第三例 留言板项目源码
- 第四例 聊天室
- 第五例 工具库
- dao
- common
- common.go
- config
- config.go
- gorm
- grom.go
- sqlx
- sqlx.go
- kafka
- kafka.go
- log
- log.go
- log2.go
- redis
- redis.go
- zookeeper
- zookeeper.go
- init
- main.go
- 工具库
- cache
- cfg.go
- redis
- 示例
- database
- cfg.go
- gorm.go
- sql.go
- 示例
- mq
- cfg.go
- kafka_consumer.go
- kafka_producter.go
- 示例
- time
- time.go
- 第六例 原生sql操作
- 第七例 sqlx操作
- 第八例 Redis数据库(gomodule/redigo)
- 第九例 Redis消息队列
- 第十例 Redis集群连接
- 十一例 Zookeeper操作
- 十二例 Kafka操作
- 十三例 NSQ操作
- 十四例 二分查找
- 十五例 交换排序 - 冒泡排序
- 十六例 插入排序 - 直接插入排序
- 十七例 插入排序 - 希尔排序
- 十八例 交换排序 - 快速排序
- 十九例 算法求解应用
- 二十例 pprof性能分析
- 二一例 CPU信息采集
- 二二例 Heap信息采集
- 二三例 Http信息采集
- 二四例 单元测试(功能测试)
- 二五例 基准测试(压力测试/性能测试)
- 二六例 gdb调试
- 二七例 json序列化和反序列化
- 二八例 protobuf序列化和反序列化
- 二九例 包管理工具 go vendor
- 三十例 包管理工具 go mod
- 三一例 zip压缩
- 三二例 交叉编译
- 三三例 线上环境部署
- 三四例 业务:实现固定周期维护
- 三五例 聊天室(精简版)
- 三六例 并发安全字典
- 三七例 导出Excel表格
- 三八例 导出CSV表格
- 三九例 聊天室(高并发)
- 四十例 JWT (Json Web Token)
- 四一例 雪花算法生成 Id
- 四二例 对称加密 AES
- 四三例 非对称加密 RSA
- 四四例 签名算法 SHA1
- 四五例 数据库操作 gorm
- gorm V2
- 四六例 数据库操作 gorm 集合
- 数据库连接和创建表
- 查询 - 分页
- 查询所有数据
- 查询单条数据
- 插入一条或多条数据
- 更新一条或多条数据
- 更新一条或多条数据(有零值)
- 四七例 RSA(MD5WithRSA 算法)签名和验签方式
- 四八例 线上部署脚本
- 四九例 Elasticsearch
- 五十例 对象池
- 五一例 中间库(github.com/wong-winnie/library)
- 五二例 二维码(生成和解析)
- 五三例 回调用例
- 五四例 文件服务器(MINIO)
- 五五例 chm文档转json
- 提取内容页Json
- 将目录索引和内容页混合生成Json
- 目录层级小案例
- 五六例 部署 gogs 代码管理工具
- 五七例 通过命令行操作SVN
- 五八例 根据数据库表生产模型
- 五九例 Trie树
- 六十例 二进制排序
- 六一例 递归+迭代实现无限级分类
- 六二例 Arrow 数据结构
- 简单介绍
- Go 用Arrow数据格式与其它语言交互
- 六三例 LMDB 内存映射型数据库
- 获取指定Key位置
- 六四例 切片数据按字段分类
- 六五例 Xorm 批量插入数据
- 六六例 FlatBuffers 序列化和反序列化
- FlatBuffers 步骤1
- FlatBuffers 步骤2
- 六七例 数据同步
- 增量同步v1
- 全量同步v1
- 定时器
- 六八例 Http请求
- 六九例 Gin + 数据库操作
- 七十例 ClickHouse 列式数据库
- 七一例 用图表展示数据库数据
- 七二例 go:linkname
- 七三例 四舍五入、保留3小数位
- 七四例 判断两个时间戳是否同一天
- 七五例 Gin Http请求
- 七六例 过滤器
- 七七例 Excel 导入导出
- 七八例 小程序向公众号推消息
- 七九列 解析二进制数据
- 例子一
- 例子二
- 八十例 路由转发
- 八一例 协程池(安全执行任务,捕获异常)
- 八二例 切片 slice
- 八三例 集合 map
- 八四例 Redis 六种数据类型
- 八五例 Zstd压缩
- 八六例 提高接口并发量
- 八七例 协程 goroutine 和 通道 channel
- 八七例 Mysql 事务和索引等
- 编写中
- 数据交互
- mysql 索引和事务
- 发请求
- defer
- 其它
- linux
- OAuth2.0 和 JWT
- 其它2
- 其他
- Web3.0 智能合约
- 多人贪吃蛇
- V1
- 客户端
- 服务端
- V2
- 同步方式
- 游戏框架
- deepseek
- k8s
- TRPC
- Kafka
- 加密
- mm
- 技术扩展阅读