go语言处理TCP拆包/粘包的具体实现
作者:熊纪元 发布时间:2023-08-25 12:19:00
标签:go语言,TCP,拆包,粘包
part 1
最近在学习go自带的rpc,看完了一遍想着自己实现一个codec,也就是自定义消息的序列化和反序列化。消息的序列化和反序列化涉及到两步:
1、从网络中读取数据和将数据写到网络中;
2、根据拿到的二进制数据反序列化以及把现有的对象序列化成二进制数据。而这个过程中就需要处理TCP的拆包粘包了。
TCP的拆包/粘包也算是网络编程中一个比较基础的问题了,具体的问题含义和解决方式也不再详细描述了。虽然作为实现应用层逻辑的程序员可能根本不需要关心这个,作为中间件的开发,同时也出于学习go语言的目的,还是稍微进行一下练习。
part 2
TCP拆包粘包的解决思路:在读取数据时,将读取到的二进制数据在正确的位置进行分割即可。这里直接使用head+body的方式,也就是在发送数据时先将整个数据的大小附加在数据前,就像这样:
+++++++++++++++++++++++++++++++++++++
size (2 bytes) | body (size bytes)
+++++++++++++++++++++++++++++++++++++
这里将数据包的size作为head
注:这里举的例子里,size占2个byte;size具体占用的byte数可以根据实际情况决定
part 3
server具体实现:
func doConn(conn net.Conn) {
var (
buffer = bytes.NewBuffer(make([]byte, 0, BUF_SIZE)) //buffer用来缓存读取到的数据
readBytes = make([]byte, BUF_SIZE) //readBytes用来接收每次读取的数据,每次读取完成之后将readBytes添加到buffer中
isHead = true //用来标识当前的状态:正在处理size部分还是body部分
bodyLen = 0 //表示body的长度
)
for {
//首先读取数据
readByteNum, err := conn.Read(readBytes)
if err != nil {
log.Fatal(err)
return
}
buffer.Write(readBytes[0:readByteNum])//将读取到的数据放到buffer中
// 然后处理数据
for {
if isHead {
if buffer.Len() >= HEAD_SIZE {
isHead = false
head := make([]byte, HEAD_SIZE)
_, err = buffer.Read(head)
if err != nil {
log.Fatal(err)
return
}
bodyLen = int(binary.BigEndian.Uint16(head))
} else {
break;
}
}
if !isHead {
if buffer.Len() >= bodyLen {
body := make([]byte, bodyLen)
_, err = buffer.Read(body[:bodyLen])
if err != nil {
log.Fatal(err)
return
}
fmt.Println("received body: " + string(body[:bodyLen]))
isHead = true
} else {
break;
}
}
}
}
func HandleTcp() {
listener, err := net.Listen("tcp", ":1234")
if err != nil {
log.Fatal(err)
return
}
log.Println("start listening on 1234")
for {
conn, err := listener.Accept()
if err != nil {
log.Fatal(err)
return
}
go doConn(conn)
}
}
client具体实现:
func SendStringwithTcp(arg string) error {
conn, err := net.Dial("tcp", ":1234")
if err != nil {
log.Fatal(err)
return err
}
head := make([]byte, server.HEAD_SIZE)
content := []byte(arg)
headSize := len(content)
binary.BigEndian.PutUint16(head, uint16(headSize))
//先写入head部分,再写入body部分
_, err = conn.Write(head)
if err != nil {
log.Fatal(err)
return err
}
_, err = conn.Write(content)
if err != nil {
log.Fatal(err)
return err
}
return nil
}
来源:https://juejin.cn/post/6844903778219458567
0
投稿
猜你喜欢
- 本文实例讲述了Python操作MySQL数据库的两种方式。分享给大家供大家参考,具体如下:第一种 使用pymysql代码如下:import
- 一个等号 =:表示赋值 ;两个等号 ==:先转换类型再比较 ;三个等号 ===:先判断类型,如果不是同一类型直接false。
- 企业最有价值的资产通常是其数据库中的客户或产品信息。因此,在这些企业中,数据库管理的一个重要部分就是保护这些数据免受外部攻击,及修复软/硬件
- 我遇到的一个小需求,就是希望通过判断pandas dataframe中一列的值在两个条件范围(比如下面代码中所描述的逻辑,取小于u-3ε和大
- 有时候我们传.py文件给别人时,需要添加一些文件头注释。为了不用每次新建文件时都去手动添加作者、创建日期等信息,我们可以设置一套模板,在新建
- Python中的切片知识.在Python中,切片(slice)是对序列型对象(如list, string, tuple)的一种高级索引方法。
- 1.简单检索数据博客内容中student表为:1.1.检索单个列select + 列名 + from + 表名1.2.检索多个列select
- 前言:话说,我一直没能理解美工究竟是什么(这是一篇投稿)。因为要求确实很多。目前,我只能理解成,是前端开发+页面设计+用户体验设计的全能手。
- Douglas Crockford是JavaScript开发社区最知名的权威,是JSON、JSLint、JSMin和ADSafe之父,是《J
- 迭代器什么是迭代迭代是重复反馈过程的活动,其目的通常是为了逼近所需目标或结果。每一次对过程的重复称为一次“迭代&
- 历史:Message Queue的需求由来已久,80年代最早在金融交易中,高盛等公司采用Teknekron公司的产品,当时的Message
- 霍夫变换是一种检测任何形状的流行技术,可以检测形状,即使它被破坏或扭曲一点点.一条线可以表示成y = mx + c或参数形式,像ρ=xcos
- 本篇文章将介绍在 Python 中将 hex 转换为 base64 的方法。使用 Python 中的 codecs 模块将 HEX 转换为
- 最近服务器时不时出现Nginx 502 Bad Gateway,如果在电脑旁边还好,要是半夜或者出去了,怎么办?没关系,写个脚本检测服务状态
- 概要说明:自动更新程序主要负责从服务器中获取相应的更新文件,并且把这些文件下载到本地,替换现有的文件。达到修复Bug,更新功能的目的。本文作
- 以下以 IE 代替 Internet Explorer,以 MF 代替 
- 前言在应用程序开发的过程中,有的时候需要在代码中保存一些机密的信息,比如加密密钥,字符串,或者是用户名密码等。通常的做法是保存到一个配置文件
- print函数python输出主要使用print函数,print函数介绍:print(*objects, sep=' ',
- 前言每个人写脚本时的格式都会有所不同,有的会注明脚本本身的一些信息,有的则开门见山,这在小团队里其实没什么,基本别人做什么你也都知道,但如果
- 如果想设置相同的初值和想要的长度>>> a=[None]*4>>> print(a)[None, Non