现在我们需要把封包和拆包的功能集成到Zinx中,并且测试Zinx该功能是否生效。

A) Request字段修改

首先我们要将我们 之前的Request中的[]byte类型的data字段 改成 Message类型.

zinx/znet/request.go

package znet

import "zinx/ziface"

type Request struct {
    conn ziface.IConnection //已经和客户端建立好的 链接

    //Message类型
    msg ziface.IMessage     //客户端请求的数据
}

//获取请求连接信息
func(r *Request) GetConnection() ziface.IConnection {
    return r.conn
}
//获取请求消息的数据
func(r *Request) GetData() []byte {
    return r.msg.GetData()
}

//获取请求的消息ID
func (r *Request) GetMsgID() uint32 {
    return r.msg.GetMsgId()
}

B) 集成拆包 过程

接下来我们需要在Connection的StartReader()方法中,修改之前的读取客户端的这段代码:
改成如下:

zinx/znet/connection.go

StartReader()方法

func (c *Connection) StartReader() {
    fmt.Println("Reader Goroutine is  running")
    defer fmt.Println(c.RemoteAddr().String(), " conn reader exit!")
    defer c.Stop()

    for  {
        //创建拆包解包的对象
        //dp = Data Pack
        dp := NewDataPack()

        //读取客户端的Msg head
        headData := make([]byte, dp.GetHeadLen())
        //从 c.GetTCPConnection() 连接中,以二进制方式读取数据到 headData[]
        if _, err := io.ReadFull(c.GetTCPConnection(), headData); err != nil {
            fmt.Println("read msg head error ", err)
            c.ExitBuffChan <- true
            continue
        }

        //将 headData[] 拆包,得到 msgid 和 datalen 放在 msg 中
        msg , err := dp.Unpack(headData)
        if err != nil {
            fmt.Println("unpack error ", err)
            c.ExitBuffChan <- true
            continue
        }

        //根据 dataLen 读取 data,放在msg.Data中
        var data []byte
        if msg.GetDataLen() > 0 {
            //根据上面得到的数据长度,开辟对应大小的 data[]
            data = make([]byte, msg.GetDataLen())
            //继续从 c.GetTCPConnection() 连接中读取数据到 data[]
            if _, err := io.ReadFull(c.GetTCPConnection(), data); err != nil {
                fmt.Println("read msg data error ", err)
                c.ExitBuffChan <- true
                continue
            }
        }
        //将 data[] 数据放到 msg结构的Data属性中:
        msg.SetData(data)

        //最后将上面的数据封装为 Request结构类型,目的是为了下面调用 c.Router.**(request) 作为参数传递进去。
        //得到当前客户端请求的Request数据
        req := Request{
            conn:c,
            msg:msg, //将之前的buf 改成 msg
        }
        //从路由Routers 中找到注册绑定Conn的对应Handle
        go func (request ziface.IRequest) {
            //执行注册的路由方法
            c.Router.PreHandle(request)
            c.Router.Handle(request)
            c.Router.PostHandle(request)
        }(&req)
    }
}

C) 提供封包 方法

现在我们已经将拆包的功能集成到Zinx中了,但是使用Zinx的时候,如果我们希望给用户返回一个TLV格式的数据,总不能每次都经过这么繁琐的过程,所以我们应该给Zinx提供一个封包的接口,供Zinx发包使用

zinx/ziface/iconnection.go

新增SendMsg()方法

package ziface

import "net"

//定义连接接口
type IConnection interface {
    //启动连接,让当前连接开始工作
    Start()
    //停止连接,结束当前连接状态M
    Stop()
    //从当前连接获取原始的socket TCPConn
    GetTCPConnection() *net.TCPConn
    //获取当前连接ID
    GetConnID() uint32
    //获取远程客户端地址信息
    RemoteAddr() net.Addr

    //新增:
    //直接将Message数据发送数据给远程的TCP客户端
    SendMsg(msgId uint32, data []byte) error
}

zinx/znet/connection.go

SendMsg()方法实现:

//直接将 Message数据 发送数据给 远程的TCP客户端
func (c *Connection) SendMsg(msgId uint32, data []byte) error {
    if c.isClosed == true {
        return errors.New("Connection closed when send msg")
    }
    //将data[]封包为msg,然后发送给客户端:
    //封包: 二进制->Message类型
    dp := NewDataPack()
    msg, err := dp.Pack(NewMsgPackage(msgId, data))
    if err != nil {
        fmt.Println("Pack error msg id = ", msgId)
        return  errors.New("Pack error msg ")
    }
    //发送:
    //写回客户端
    if _, err := c.Conn.Write(msg); err != nil {
        fmt.Println("Write msg id ", msgId, " error ")
        c.ExitBuffChan <- true
        return errors.New("conn Write error")
    }

    return nil
}