当我们在使用连接处理的时候,希望和连接绑定一些用户的数据,或者参数。那么我们现在可以把当前连接设定一些传递参数的接口或者方法。
10.1 给连接添加连接配置接口
zinx/ziface/iconnection.go
//定义连接接口
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
//直接将Message数据发送给远程的TCP客户端(有缓冲)
SendBuffMsg(msgId uint32, data []byte) error
//设置连接属性
SetProperty(key string, value interface{})
//获取连接属性
GetProperty(key string)(interface{}, error)
//移除连接属性
RemoveProperty(key string)
}
这里增添了3个方法SetProperty(),GetProperty(),RemoveProperty().那么property是什么类型的呢,我么接下来看看Connection的定义。
10.2 链接属性方法实现
zinx/znet/connction.go
type Connection struct {
//当前Conn属于哪个Server
TcpServer ziface.IServer
//当前连接的socket TCP套接字
Conn *net.TCPConn
//当前连接的ID 也可以称作为SessionID,ID全局唯一
ConnID uint32
//当前连接的关闭状态
isClosed bool
//消息管理MsgId和对应处理方法的消息管理模块
MsgHandler ziface.IMsgHandle
//告知该连接已经退出/停止的channel
ExitBuffChan chan bool
//无缓冲管道,用于读、写两个goroutine之间的消息通信
msgChan chan []byte
//有关冲管道,用于读、写两个goroutine之间的消息通信
msgBuffChan chan []byte
// ================================
//连接属性
property map[string]interface{}
//保护连接属性修改的锁
propertyLock sync.RWMutex
// ================================
}
//创建连接的方法
func NewConntion(server ziface.IServer, conn *net.TCPConn, connID uint32, msgHandler ziface.IMsgHandle) *Connection {
//初始化Conn属性
c := &Connection{
TcpServer: server,
Conn: conn,
ConnID: connID,
isClosed: false,
MsgHandler: msgHandler,
ExitBuffChan: make(chan bool, 1),
msgChan: make(chan []byte),
msgBuffChan: make(chan []byte, utils.GlobalObject.MaxMsgChanLen),
property: make(map[string]interface{}), //对连接属性map初始化
}
//将新创建的Conn添加到链接管理中
c.TcpServer.GetConnMgr().Add(c)
return c
}
// ...
//设置连接属性
func (c *Connection) SetProperty(key string, value interface{}) {
c.propertyLock.Lock()
defer c.propertyLock.Unlock()
c.property[key] = value
}
//获取连接属性
func (c *Connection) GetProperty(key string) (interface{}, error) {
c.propertyLock.RLock()
defer c.propertyLock.RUnlock()
if value, ok := c.property[key]; ok {
return value, nil
} else {
return nil, errors.New("no property found")
}
}
//移除连接属性
func (c *Connection) RemoveProperty(key string) {
c.propertyLock.Lock()
defer c.propertyLock.Unlock()
delete(c.property, key)
}
10.3 连接属性Zinx-V0.10单元测试
那么,接下来,我们简单测试一下连接属性的设置与提取是否可用。
Server.go
package main
import (
"fmt"
"zinx/ziface"
"zinx/znet"
)
//ping test 自定义路由
type PingRouter struct {
znet.BaseRouter
}
//Ping Handle
func (this *PingRouter) Handle(request ziface.IRequest) {
fmt.Println("Call PingRouter Handle")
//先读取客户端的数据,再回写ping...ping...ping
fmt.Println("recv from client : msgId=", request.GetMsgID(), ", data=", string(request.GetData()))
err := request.GetConnection().SendBuffMsg(0, []byte("ping...ping...ping"))
if err != nil {
fmt.Println(err)
}
}
type HelloZinxRouter struct {
znet.BaseRouter
}
//HelloZinxRouter Handle
func (this *HelloZinxRouter) Handle(request ziface.IRequest) {
fmt.Println("Call HelloZinxRouter Handle")
//先读取客户端的数据,再回写ping...ping...ping
fmt.Println("recv from client : msgId=", request.GetMsgID(), ", data=", string(request.GetData()))
err := request.GetConnection().SendBuffMsg(1, []byte("Hello Zinx Router V0.10"))
if err != nil {
fmt.Println(err)
}
}
//创建连接的时候执行
func DoConnectionBegin(conn ziface.IConnection) {
fmt.Println("DoConnecionBegin is Called ... ")
//=============设置两个连接属性,在连接创建之后===========
fmt.Println("Set conn Name, Home done!")
conn.SetProperty("Name", "Aceld")
conn.SetProperty("Home", "https://www.jianshu.com/u/35261429b7f1")
//===================================================
err := conn.SendMsg(2, []byte("DoConnection BEGIN..."))
if err != nil {
fmt.Println(err)
}
}
//连接断开的时候执行
func DoConnectionLost(conn ziface.IConnection) {
//============在连接销毁之前,查询conn的Name,Home属性=====
if name, err:= conn.GetProperty("Name"); err == nil {
fmt.Println("Conn Property Name = ", name)
}
if home, err := conn.GetProperty("Home"); err == nil {
fmt.Println("Conn Property Home = ", home)
}
//===================================================
fmt.Println("DoConneciotnLost is Called ... ")
}
func main() {
//创建一个server句柄
s := znet.NewServer()
//注册连接hook回调函数
s.SetOnConnStart(DoConnectionBegin)
s.SetOnConnStop(DoConnectionLost)
//配置路由
s.AddRouter(0, &PingRouter{})
s.AddRouter(1, &HelloZinxRouter{})
//开启服务
s.Serve()
}
这里主要看DoConnectionBegin()和DoConnectionLost()两个函数的实现, 利用在两个Hook函数中,设置连接属性和提取连接属性。连接创建之后给当前连接绑定两个属性”Name”,”Home”, 那么我们在随时可以通过conn.GetProperty()方法得到连接已经设置的属性。
$go run Server.go
$go run Client0.go
服务端:
$ go run Server.go
Add api msgId = 0
Add api msgId = 1
[START] Server name: zinx v-0.10 demoApp,listenner at IP: 127.0.0.1, Port 7777 is starting
[Zinx] Version: V0.4, MaxConn: 3, MaxPacketSize: 4096
start Zinx server zinx v-0.10 demoApp succ, now listenning...
Worker ID = 9 is started.
Worker ID = 5 is started.
Worker ID = 6 is started.
Worker ID = 7 is started.
Worker ID = 8 is started.
Worker ID = 1 is started.
Worker ID = 0 is started.
Worker ID = 2 is started.
Worker ID = 3 is started.
Worker ID = 4 is started.
connection add to ConnManager successfully: conn num = 1
---> CallOnConnStart....
DoConnecionBegin is Called ...
Set conn Name, Home done!
[Writer Goroutine is running]
[Reader Goroutine is running]
Add ConnID= 0 request msgID= 0 to workerID= 0
Call PingRouter Handle
recv from client : msgId= 0 , data= Zinx V0.8 Client0 Test Message
Add ConnID= 0 request msgID= 0 to workerID= 0
Call PingRouter Handle
recv from client : msgId= 0 , data= Zinx V0.8 Client0 Test Message
Add ConnID= 0 request msgID= 0 to workerID= 0
Call PingRouter Handle
recv from client : msgId= 0 , data= Zinx V0.8 Client0 Test Message
read msg head error read tcp4 127.0.0.1:7777->127.0.0.1:55208: read: connection reset by peer
Conn Stop()...ConnID = 0
---> CallOnConnStop....
Conn Property Name = Aceld
Conn Property Home = https://www.jianshu.com/u/35261429b7f1
DoConneciotnLost is Called ...
connection Remove ConnID= 0 successfully: conn num = 0
127.0.0.1:55208 [conn Reader exit!]
127.0.0.1:55208 [conn Writer exit!]
客户端:
$ go run Client0.go
Client Test ... start
==> Recv Msg: ID= 2 , len= 21 , data= DoConnection BEGIN...
==> Recv Msg: ID= 0 , len= 18 , data= ping...ping...ping
==> Recv Msg: ID= 0 , len= 18 , data= ping...ping...ping
^Csignal: interrupt
当我们终止客户端连接,那么服务端在断开连接之前,已经读取到了conn的两个属性Name和Home。