tcp (transport control protocol,传输控制协议)是面向连接的,面向流的,提供高可靠性服务。收发两端(客户端和服务器端)都要有一一成对的socket,因此,发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。这样,接收端,就难于分辨出来了,必须提供科学的拆包机制。 即面向流的通信是无消息保护边界的
处理方法一般有两种处理方式,一种为发送数据流用分隔符,另一种在客户端发包的时候定义包体的长度,服务端按照一定的规则解包
栗子-server端package main
import (
" bytes "
" encoding /binary"
"fmt"
"net"
"unsafe"
"errors"
)
var (
pkgHeaderLen int
ErrNotEnoughData = errors.New("packet stream is not enough")
)
// 用来接收tcp 数据流
var buffer *bytes.Buffer
func init() {
buffer = new(bytes.Buffer)
// 自定义包头长度
pkgHeaderLen = (int)((uint)(unsafe.Sizeof(PackageHeader{})))
}
type PackageHeader struct {
ServiceID uint32 // service id
Command uint32 // operation command code
Code int32 // error code
Len uint16 // body length
}
type Package struct {
H Package Header
B string
}
func (p Package) Marshal() (* byte s.Buffer, error) {
var (
err error
buf *bytes.Buffer
)
buf = &bytes.Buffer{}
err = binary.Write(buf, binary.LittleEndian, p.H)
if err != nil {
return nil, err
}
buf.WriteByte((byte)(len(p.B)))
buf.WriteString(p.B)
return buf, nil
}
func (p *Package) Unmarshal(buf *bytes.Buffer) (int, error) {
var (
err error
len byte
)
// header
err = binary.Read(buf, binary.LittleEndian, &(p.H))
if err != nil {
return 0, err
}
// 获取包体长度
len, err = buf.ReadByte()
if err != nil {
return 0, nil
}
// 是否是包体的长度
if buf.Len() < (int)(p.H.Len) {
return 0, ErrNotEnoughData
}
// 获取具体数据,并把buffer 的offset后置
p.B = (string)(buf.Next((int)(len)))
return (int)(p.H.Len) + pkgHeaderLen, nil
}
func main() {
netListen, _ := net.Listen("tcp", ":9988")
defer netListen.Close()
fmt.Println("Waiting for clients")
for {
conn, err := netListen.Accept()
if err != nil {
continue
}
fmt.Println(conn.RemoteAddr().String(), " tcp connect success")
go handleConnection(conn)
}
}
func handleConnection(conn net.Conn) {
pkg := &Package{}
buf := make([]byte, 1024)
for {
n, err := conn.Read(buf)
if err != nil {
fmt.Println(conn.RemoteAddr().String(), " connection error: ", err)
return
}
// 一直往buffer 写数据, 然后在按照规则解包
buffer.Write(buf)
// 解码包体长度
length , err :=pkg.Unmarshal(bytes.NewBuffer(buf))
if err == nil {
buffer.Next(n)
fmt.Println("==========")
fmt.Printf("pkg len %d, %s\n",length, pkg.B)
fmt.Println("==========")
} else {
fmt.Println(err.Error())
}
}
}