现在我们需要把封包和拆包的功能集成到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()方法中,修改之前的读取客户端的这段代码:

  1. func (c *Connection) StartReader() {
  2. //...
  3. for {
  4. //读取我们最大的数据到buf中
  5. buf := make([]byte, utils.GlobalObject.MaxPacketSize)
  6. _, err := c.Conn.Read(buf)
  7. if err != nil {
  8. fmt.Println("recv buf err ", err)
  9. c.ExitBuffChan <- true
  10. continue
  11. }
  12. //...
  13. }
  14. }

改成如下:

zinx/znet/connection.go

StartReader()方法

  1. func (c *Connection) StartReader() {
  2. fmt.Println("Reader Goroutine is running")
  3. defer fmt.Println(c.RemoteAddr().String(), " conn reader exit!")
  4. defer c.Stop()
  5. for {
  6. // 创建拆包解包的对象
  7. dp := NewDataPack()
  8. //读取客户端的Msg head
  9. headData := make([]byte, dp.GetHeadLen())
  10. if _, err := io.ReadFull(c.GetTCPConnection(), headData); err != nil {
  11. fmt.Println("read msg head error ", err)
  12. c.ExitBuffChan <- true
  13. continue
  14. }
  15. //拆包,得到msgid 和 datalen 放在msg中
  16. msg , err := dp.Unpack(headData)
  17. if err != nil {
  18. fmt.Println("unpack error ", err)
  19. c.ExitBuffChan <- true
  20. continue
  21. }
  22. //根据 dataLen 读取 data,放在msg.Data中
  23. var data []byte
  24. if msg.GetDataLen() > 0 {
  25. data = make([]byte, msg.GetDataLen())
  26. if _, err := io.ReadFull(c.GetTCPConnection(), data); err != nil {
  27. fmt.Println("read msg data error ", err)
  28. c.ExitBuffChan <- true
  29. continue
  30. }
  31. }
  32. msg.SetData(data)
  33. //得到当前客户端请求的Request数据
  34. req := Request{
  35. conn:c,
  36. msg:msg, //将之前的buf 改成 msg
  37. }
  38. //从路由Routers 中找到注册绑定Conn的对应Handle
  39. go func (request ziface.IRequest) {
  40. //执行注册的路由方法
  41. c.Router.PreHandle(request)
  42. c.Router.Handle(request)
  43. c.Router.PostHandle(request)
  44. }(&req)
  45. }
  46. }

C) 提供封包方法

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

zinx/ziface/iconnection.go

新增SendMsg()方法

  1. package ziface
  2. import "net"
  3. //定义连接接口
  4. type IConnection interface {
  5. //启动连接,让当前连接开始工作
  6. Start()
  7. //停止连接,结束当前连接状态M
  8. Stop()
  9. //从当前连接获取原始的socket TCPConn
  10. GetTCPConnection() *net.TCPConn
  11. //获取当前连接ID
  12. GetConnID() uint32
  13. //获取远程客户端地址信息
  14. RemoteAddr() net.Addr
  15. //直接将Message数据发送数据给远程的TCP客户端
  16. SendMsg(msgId uint32, data []byte) error
  17. }

zinx/znet/connection.go

SendMsg()方法实现:

  1. //直接将Message数据发送数据给远程的TCP客户端
  2. func (c *Connection) SendMsg(msgId uint32, data []byte) error {
  3. if c.isClosed == true {
  4. return errors.New("Connection closed when send msg")
  5. }
  6. //将data封包,并且发送
  7. dp := NewDataPack()
  8. msg, err := dp.Pack(NewMsgPackage(msgId, data))
  9. if err != nil {
  10. fmt.Println("Pack error msg id = ", msgId)
  11. return errors.New("Pack error msg ")
  12. }
  13. //写回客户端
  14. if _, err := c.Conn.Write(msg); err != nil {
  15. fmt.Println("Write msg id ", msgId, " error ")
  16. c.ExitBuffChan <- true
  17. return errors.New("conn Write error")
  18. }
  19. return nil
  20. }