当我们在使用链接处理的时候,希望和链接绑定一些用户的数据,或者参数。那么我们现在可以把当前链接设定一些传递参数的接口或者方法。

10.1 给链接添加链接配置接口

zinx/ziface/iconnection.go

  1. //定义连接接口
  2. type IConnection interface {
  3. //启动连接,让当前连接开始工作
  4. Start()
  5. //停止连接,结束当前连接状态M
  6. Stop()
  7. //从当前连接获取原始的socket TCPConn
  8. GetTCPConnection() *net.TCPConn
  9. //获取当前连接ID
  10. GetConnID() uint32
  11. //获取远程客户端地址信息
  12. RemoteAddr() net.Addr
  13. //直接将Message数据发送数据给远程的TCP客户端(无缓冲)
  14. SendMsg(msgId uint32, data []byte) error
  15. //直接将Message数据发送给远程的TCP客户端(有缓冲)
  16. SendBuffMsg(msgId uint32, data []byte) error
  17. //设置链接属性
  18. SetProperty(key string, value interface{})
  19. //获取链接属性
  20. GetProperty(key string)(interface{}, error)
  21. //移除链接属性
  22. RemoveProperty(key string)
  23. }

这里增添了3个方法SetProperty(),GetProperty(),RemoveProperty().那么property是什么类型的呢,我么接下来看看Connection的定义。

10.2 链接属性方法实现

zinx/znet/connction.go

  1. type Connection struct {
  2. //当前Conn属于哪个Server
  3. TcpServer ziface.IServer
  4. //当前连接的socket TCP套接字
  5. Conn *net.TCPConn
  6. //当前连接的ID 也可以称作为SessionID,ID全局唯一
  7. ConnID uint32
  8. //当前连接的关闭状态
  9. isClosed bool
  10. //消息管理MsgId和对应处理方法的消息管理模块
  11. MsgHandler ziface.IMsgHandle
  12. //告知该链接已经退出/停止的channel
  13. ExitBuffChan chan bool
  14. //无缓冲管道,用于读、写两个goroutine之间的消息通信
  15. msgChan chan []byte
  16. //有关冲管道,用于读、写两个goroutine之间的消息通信
  17. msgBuffChan chan []byte
  18. // ================================
  19. //链接属性
  20. property map[string]interface{}
  21. //保护链接属性修改的锁
  22. propertyLock sync.RWMutex
  23. // ================================
  24. }
  25. //创建连接的方法
  26. func NewConntion(server ziface.IServer, conn *net.TCPConn, connID uint32, msgHandler ziface.IMsgHandle) *Connection {
  27. //初始化Conn属性
  28. c := &Connection{
  29. TcpServer: server,
  30. Conn: conn,
  31. ConnID: connID,
  32. isClosed: false,
  33. MsgHandler: msgHandler,
  34. ExitBuffChan: make(chan bool, 1),
  35. msgChan: make(chan []byte),
  36. msgBuffChan: make(chan []byte, utils.GlobalObject.MaxMsgChanLen),
  37. property: make(map[string]interface{}), //对链接属性map初始化
  38. }
  39. //将新创建的Conn添加到链接管理中
  40. c.TcpServer.GetConnMgr().Add(c)
  41. return c
  42. }
  43. // ...
  44. //设置链接属性
  45. func (c *Connection) SetProperty(key string, value interface{}) {
  46. c.propertyLock.Lock()
  47. defer c.propertyLock.Unlock()
  48. c.property[key] = value
  49. }
  50. //获取链接属性
  51. func (c *Connection) GetProperty(key string) (interface{}, error) {
  52. c.propertyLock.RLock()
  53. defer c.propertyLock.RUnlock()
  54. if value, ok := c.property[key]; ok {
  55. return value, nil
  56. } else {
  57. return nil, errors.New("no property found")
  58. }
  59. }
  60. //移除链接属性
  61. func (c *Connection) RemoveProperty(key string) {
  62. c.propertyLock.Lock()
  63. defer c.propertyLock.Unlock()
  64. delete(c.property, key)
  65. }

10.3 链接属性Zinx-V0.10单元测试

那么,接下来,我们简单测试一下链接属性的设置与提取是否可用。

Server.go

  1. package main
  2. import (
  3. "fmt"
  4. "zinx/ziface"
  5. "zinx/znet"
  6. )
  7. //ping test 自定义路由
  8. type PingRouter struct {
  9. znet.BaseRouter
  10. }
  11. //Ping Handle
  12. func (this *PingRouter) Handle(request ziface.IRequest) {
  13. fmt.Println("Call PingRouter Handle")
  14. //先读取客户端的数据,再回写ping...ping...ping
  15. fmt.Println("recv from client : msgId=", request.GetMsgID(), ", data=", string(request.GetData()))
  16. err := request.GetConnection().SendBuffMsg(0, []byte("ping...ping...ping"))
  17. if err != nil {
  18. fmt.Println(err)
  19. }
  20. }
  21. type HelloZinxRouter struct {
  22. znet.BaseRouter
  23. }
  24. //HelloZinxRouter Handle
  25. func (this *HelloZinxRouter) Handle(request ziface.IRequest) {
  26. fmt.Println("Call HelloZinxRouter Handle")
  27. //先读取客户端的数据,再回写ping...ping...ping
  28. fmt.Println("recv from client : msgId=", request.GetMsgID(), ", data=", string(request.GetData()))
  29. err := request.GetConnection().SendBuffMsg(1, []byte("Hello Zinx Router V0.10"))
  30. if err != nil {
  31. fmt.Println(err)
  32. }
  33. }
  34. //创建连接的时候执行
  35. func DoConnectionBegin(conn ziface.IConnection) {
  36. fmt.Println("DoConnecionBegin is Called ... ")
  37. //=============设置两个链接属性,在连接创建之后===========
  38. fmt.Println("Set conn Name, Home done!")
  39. conn.SetProperty("Name", "Aceld")
  40. conn.SetProperty("Home", "https://www.jianshu.com/u/35261429b7f1")
  41. //===================================================
  42. err := conn.SendMsg(2, []byte("DoConnection BEGIN..."))
  43. if err != nil {
  44. fmt.Println(err)
  45. }
  46. }
  47. //连接断开的时候执行
  48. func DoConnectionLost(conn ziface.IConnection) {
  49. //============在连接销毁之前,查询conn的Name,Home属性=====
  50. if name, err:= conn.GetProperty("Name"); err == nil {
  51. fmt.Println("Conn Property Name = ", name)
  52. }
  53. if home, err := conn.GetProperty("Home"); err == nil {
  54. fmt.Println("Conn Property Home = ", home)
  55. }
  56. //===================================================
  57. fmt.Println("DoConneciotnLost is Called ... ")
  58. }
  59. func main() {
  60. //创建一个server句柄
  61. s := znet.NewServer()
  62. //注册链接hook回调函数
  63. s.SetOnConnStart(DoConnectionBegin)
  64. s.SetOnConnStop(DoConnectionLost)
  65. //配置路由
  66. s.AddRouter(0, &PingRouter{})
  67. s.AddRouter(1, &HelloZinxRouter{})
  68. //开启服务
  69. s.Serve()
  70. }

这里主要看DoConnectionBegin()DoConnectionLost()两个函数的实现, 利用在两个Hook函数中,设置链接属性和提取链接属性。链接创建之后给当前链接绑定两个属性”Name”,”Home”, 那么我们在随时可以通过conn.GetProperty()方法得到链接已经设置的属性。

  1. $go run Server.go
  1. $go run Client0.go

服务端:

  1. $ go run Server.go
  2. Add api msgId = 0
  3. Add api msgId = 1
  4. [START] Server name: zinx v-0.10 demoApp,listenner at IP: 127.0.0.1, Port 7777 is starting
  5. [Zinx] Version: V0.4, MaxConn: 3, MaxPacketSize: 4096
  6. start Zinx server zinx v-0.10 demoApp succ, now listenning...
  7. Worker ID = 9 is started.
  8. Worker ID = 5 is started.
  9. Worker ID = 6 is started.
  10. Worker ID = 7 is started.
  11. Worker ID = 8 is started.
  12. Worker ID = 1 is started.
  13. Worker ID = 0 is started.
  14. Worker ID = 2 is started.
  15. Worker ID = 3 is started.
  16. Worker ID = 4 is started.
  17. connection add to ConnManager successfully: conn num = 1
  18. ---> CallOnConnStart....
  19. DoConnecionBegin is Called ...
  20. Set conn Name, Home done!
  21. [Writer Goroutine is running]
  22. [Reader Goroutine is running]
  23. Add ConnID= 0 request msgID= 0 to workerID= 0
  24. Call PingRouter Handle
  25. recv from client : msgId= 0 , data= Zinx V0.8 Client0 Test Message
  26. Add ConnID= 0 request msgID= 0 to workerID= 0
  27. Call PingRouter Handle
  28. recv from client : msgId= 0 , data= Zinx V0.8 Client0 Test Message
  29. Add ConnID= 0 request msgID= 0 to workerID= 0
  30. Call PingRouter Handle
  31. recv from client : msgId= 0 , data= Zinx V0.8 Client0 Test Message
  32. read msg head error read tcp4 127.0.0.1:7777->127.0.0.1:55208: read: connection reset by peer
  33. Conn Stop()...ConnID = 0
  34. ---> CallOnConnStop....
  35. Conn Property Name = Aceld
  36. Conn Property Home = https://www.jianshu.com/u/35261429b7f1
  37. DoConneciotnLost is Called ...
  38. connection Remove ConnID= 0 successfully: conn num = 0
  39. 127.0.0.1:55208 [conn Reader exit!]
  40. 127.0.0.1:55208 [conn Writer exit!]

客户端:

  1. $ go run Client0.go
  2. Client Test ... start
  3. ==> Recv Msg: ID= 2 , len= 21 , data= DoConnection BEGIN...
  4. ==> Recv Msg: ID= 0 , len= 18 , data= ping...ping...ping
  5. ==> Recv Msg: ID= 0 , len= 18 , data= ping...ping...ping
  6. ^Csignal: interrupt

当我们终止客户端链接,那么服务端在断开链接之前,已经读取到了conn的两个属性Name和Home