Zinx-v0.5
为了解决tcp粘包问题
消息封装模块
属性
- 消息id
- 消息的内容
- 消息的长度
抽象层
type IMessage interface {
//getter
GetMsgId() uint32
GetMsgLen() uint32
GetMsgData() []byte
//setter
SetMsgId(uint32)
SetMsgLen(uint32)
SetMsgData([]byte)
}
实现层
//
type Message struct {
Id uint32
Datalen uint32
Data []byte
}
func (m *Message) GetMsgId() uint32 {
return m.Id
}
func (m *Message) GetMsgLen() uint32 {
return m.Datalen
}
func (m *Message) GetMsgData() []byte {
return m.Data
}
//setter
func (m *Message) SetMsgId(id uint32) {
m.Id =id
}
func (m *Message) SetData(data []byte) {
m.Data = data
}
func (m *Message) SetDatalen(len uint32) {
m.Datalen = len
}
拆包封包模块
抽象层
type IDataPack interface {
//获取二进制包的头部长度 固定返回8
GetHeadLen() uint32
//封包方法 ---- 将 Message 打包成 |datalen|dataID|data|\
Pack(msg IMessage) ([]byte, error)
//拆包方法 --- 将|datalen|dataID|data| 拆解到 Message 结构体中
UnPack([]byte) (IMessage, error)
}
实现层
type DataPack struct {
}
//初始化一个DataPack对象
func NewDataPack() *DataPack {
return &DataPack{}
}
//获取二进制包的头部长度 固定返回8
func (dp *DataPack) GetHeadLen() uint32 {
//Datalen uint32(4字节) + ID uint32(4字节)
return 8
}
//封包方法 ---- 将 Message 打包成 |datalen|dataID|data|\
func (dp *DataPack) Pack(msg ziface.IMessage) ([]byte, error) {
//创建一个存放二进制的字节缓冲
dataBuffer := bytes.NewBuffer([]byte{})
//将datalen 写进buffer中
if err := binary.Write(dataBuffer, binary.LittleEndian, msg.GetMsgLen()) ; err != nil {
return nil, err
}
//将 dataID写进buffer中
if err := binary.Write(dataBuffer, binary.LittleEndian, msg.GetMsgId()) ; err != nil {
return nil, err
}
//将 data写进buffer中
if err := binary.Write(dataBuffer, binary.LittleEndian, msg.GetMsgData()) ; err != nil {
return nil, err
}
//返回这个缓冲
return dataBuffer.Bytes(), nil
}
//拆包方法 --- 将|datalen|dataID|data| 拆解到 Message 结构体中
func (dp *DataPack) UnPack(binaryData []byte) (ziface.IMessage, error) {
//解包的时候 是分2次解压, 第一次读取固定的长度8字节, 第二次是根据len 再次进行read
msgHead := &Message{} //msgHead.Datalen, msgHead.dataID
//创建一个 读取二进制数据流的io.Reader
dataBuff := bytes.NewReader(binaryData)
//将二进制流 先读datalen 放在msg的DataLen属性中
if err := binary.Read(dataBuff, binary.LittleEndian, &msgHead.Datalen); err != nil {
return nil, err
}
//将二进制流的 DataID 方在Msg的DataID属性中
if err := binary.Read(dataBuff, binary.LittleEndian, &msgHead.Id); err != nil {
return nil, err
}
return msgHead, nil
}
修改
修改request对象
接口
type IRequest interface {
//得到当前请求的链接
GetConnection() IConnection
//得到消息
GetMessage() IMessage
}
结构体
type request struct {
//链接
conn ziface.IConnection
//消息
msg ziface.IMessage
}
构造函数
//构造方法
func NewRequest(conn ziface.IConnection, msg ziface.IMessage) ziface.IRequest {
r := &request{
conn,
msg,
}
return r
}
修改connection
读取数据,解决tcp粘包
-
第一次读取
指定长度读取
只读8个字节
-
第二次读取
根据数据长度读取数据内容
func (c *Connection) startReader() {
//从对端读数据
fmt.Println("Reader go is startin....")
defer fmt.Println("connID = ", c.connID, "Reader is exit, remote addr is = ", c.GetRemoteAddr().String())
defer c.Stop()
for {
//创建拆包封包对象
dp := new(DataPack)
//读取客户端消息的头部
msgHead := make([]byte, dp.GetHeadLen())
_, err := io.ReadFull(c.GetTCPConnection(), msgHead)
if err != nil {
fmt.Println("read msg head error:", err)
return
}
msg, err := dp.UnPack(msgHead)
if err != nil {
fmt.Println("unpack msg error:", err)
return
}
//根据头部,获取数据的长度,读取消息的数据部分
msgBody := make([]byte, msg.GetMsgLen())
if _, err := io.ReadFull(c.GetTCPConnection(), msgBody); err != nil {
fmt.Println("read msg body error:", err)
return
}
msg.SetMsgData(msgBody)
//将当前一次性得到的对端客户端请求的数据,封装成一个Request
req := NewRequest(c, msg)
//路由处理业务
go func() {
c.router.PreHandle(req)
c.router.Handle(req)
c.router.PostHandle(req)
}()
//将数据 传递给我们 定义好的Handle Callback方法
//if err := c.handleAPI(req); err != nil {
// fmt.Println("ConnID", c.connID, "Handle is error", err)
// break
//}
}
}
func (c *Connection)Send(msgId uint32, msgData []byte) error {
//判断链接是否结束
if c.isClosed == true {
return errors.New("connection is closed....")
}
dp := new(DataPack)
//封装成msg
binary, err := dp.Pack(NewMessage(msgId, msgData))
if err != nil {
fmt.Println("pack err msg id:", msgId)
return err
}
//将binaryMsg发送给对端
if _, err := c.GetTCPConnection().Write(binary); err != nil {
fmt.Println("conn write err msg id:",msgId)
return err
}
return nil
}