解决tcp粘包拆包问题,有多种处理方案,这里在数据包中添加数据包长度字段,按长度读取字节数据得到完整的数据包。定义的方法如下:
完整Demo下载:
https://github.com/itchin/bytes-packet
bytes-packet.go
package bpacket
import (
//"fmt"
"bytes"
"encoding/binary"
)
var (
// 完整的数据包
packet = make([]byte, 0)
// 缓存
cache = make([]byte, 0)
)
const (
// 每个数据包前4个字节保存一个整形,该整形记录数据包的字节长度
HEADER_LEN = 4
)
// 打包
func Packet(buf []byte) []byte {
return append(intToBytes(len(buf)), buf...)
}
// 解包
func Unpack(buf []byte) ([]byte) {
buf = append(cache, buf...)
length := len(buf)
messageLength := bytesToInt(buf[:HEADER_LEN])
// 当前数据包的总长度
total := HEADER_LEN + messageLength
if length < total {
cache = buf
return []byte{}
} else if (length == total) {
cache = []byte{}
return buf[HEADER_LEN:]
} else {
cache = buf[total:]
return buf[HEADER_LEN:total]
}
}
//整形转换成字节
func intToBytes(n int) []byte {
x := int32(n)
bytesBuffer := bytes.NewBuffer([]byte{})
binary.Write(bytesBuffer, binary.BigEndian, x)
return bytesBuffer.Bytes()
}
//字节转换成整形
func bytesToInt(b []byte) int {
bytesBuffer := bytes.NewBuffer(b)
var x int32
binary.Read(bytesBuffer, binary.BigEndian, &x)
return int(x)
}
Demo:
server.go
package main
import (
"fmt"
"io"
"net"
"github.com/itchin/bytes-packet"
)
func main() {
server, err := net.Listen("tcp", ":9000")
if err != nil {
panic(err)
}
defer server.Close()
for {
conn, err := server.Accept()
if err != nil {
continue
}
defer conn.Close()
go run(conn)
}
}
func run(conn net.Conn) {
// 每次读取的字节流
buf := make([]byte, 1)
for {
n, err := conn.Read(buf)
if err != nil || err == io.EOF {
conn.Close()
break
}
packet := bpacket.Unpack(buf[:n])
if len(packet) > 0 {
fmt.Println(string(packet))
}
}
}
client.go
package main
import (
"fmt"
"github.com/itchin/bytes-packet"
"net"
"time"
)
func main() {
conn, err := net.Dial("tcp", "127.0.0.1:9000")
if err != nil {
fmt.Printf("connect failed, err : %v\n", err.Error())
return
}
defer conn.Close()
msg := "hello world"
conn.Write(bpacket.Packet([]byte(msg)))
time.Sleep(time.Second / 10)
}
有疑问加站长微信联系(非本文作者)