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

A) Request字段修改

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

zinx/znet/request.go

  1. package znet
  2. import "zinx/ziface"
  3. type Request struct {
  4. conn ziface.IConnection //已经和客户端建立好的 链接
  5. msg ziface.IMessage //客户端请求的数据
  6. }
  7. //获取请求连接信息
  8. func(r *Request) GetConnection() ziface.IConnection {
  9. return r.conn
  10. }
  11. //获取请求消息的数据
  12. func(r *Request) GetData() []byte {
  13. return r.msg.GetData()
  14. }
  15. //获取请求的消息的ID
  16. func (r *Request) GetMsgID() uint32 {
  17. return r.msg.GetMsgId()
  18. }

B) 集成拆包过程

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

func (c *Connection) StartReader() {

    //...

    for  {
        //读取我们最大的数据到buf中
        buf := make([]byte, utils.GlobalObject.MaxPacketSize)
        _, err := c.Conn.Read(buf)
        if err != nil {
            fmt.Println("recv buf err ", err)
            c.ExitBuffChan <- true
            continue
        }

        //...

    }
}

改成如下:

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 := NewDataPack()

        //读取客户端的Msg head
        headData := make([]byte, dp.GetHeadLen())
        if _, err := io.ReadFull(c.GetTCPConnection(), headData); err != nil {
            fmt.Println("read msg head error ", err)
            c.ExitBuffChan <- true
            continue
        }

        //拆包,得到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 = make([]byte, msg.GetDataLen())
            if _, err := io.ReadFull(c.GetTCPConnection(), data); err != nil {
                fmt.Println("read msg data error ", err)
                c.ExitBuffChan <- true
                continue
            }
        }
        msg.SetData(data)

        //得到当前客户端请求的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封包,并且发送
    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
}