基于GO语言如何解决TCP拆包粘包的问题
导读:相信很多人对“基于GO语言如何解决TCP拆包粘包的问题”都不太了解,下面小编为你详细解释一下这个问题,希望对你有一定的帮助 part 1 最近在学习go自带的rpc,看完了一遍想着自己实现一个codec,也就是自定...
相信很多人对“基于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
}
关于“基于GO语言如何解决TCP拆包粘包的问题”的内容就介绍到这,感谢各位的阅读,相信大家对基于GO语言如何解决TCP拆包粘包的问题已经有了进一步的了解。大家如果还想学习更多知识,欢迎关注网络,小编将为大家输出更多高质量的实用文章!
声明:本文内容由网友自发贡献,本站不承担相应法律责任。对本内容有异议或投诉,请联系2913721942#qq.com核实处理,我们将尽快回复您,谢谢合作!
若转载请注明出处: 基于GO语言如何解决TCP拆包粘包的问题
本文地址: https://pptw.com/jishu/653314.html
