Golang网络游戏协议处理框架
http://studygolang.com/articles/4350
应用背景:
- 玩家连接登录服务器
- 登录服务器向数据库请求玩家数据
- 登录服务器获取到玩家数据,把玩家数据转发给游戏服务器进行加载包括创建玩家对象等
- 登录服务器获取到加载成功回应后,通知玩家客户端可以进入游戏世界
- 发送一个数据包并等待回应。比如登录服务器等待游戏服务器加载玩家数据的结果通知。
- 发送一个数据包,不需要回应。比如游戏服务器加载玩家数据后,给登录服务器发送结果通知。
- 如果一个协议包需要等待回应,就在调用函数上阻塞等待。这个调用的签名为:
func (p *Connection) Query(data []byte) ([]byte, error)
注意:data的控制权会转交给框架,因此函数调用后不能修改data的内容。 - 如果发送一个协议包是对于接收到的某个协议包的回应,则调用:
func (p *Connection) Reply(query, answer []byte) error
注意:answer的控制权会转交给框架,因此函数调用后不能修改answer的内容。 - 如果一个协议包不需要回应,就直接调用发送函数:
func (p *Connection) Write(data []byte) error
注意:data的控制权会转交给框架,因此函数调用后不能修改data的内容。 - 调用者需要实现的接口:
- Socket。用于协议包的收发。基本上是net.TCPConn的简单封装,在头部加上一个协议包的长度。
- DataHandler。用于协议处理,即没有通过Query返回的协议包会分发给此接口处理。
- ErrorHandler。用于错误处理。当断线时,会调用此接口。
- IdentityHandler。用于读取和设置会话ID。
ErrorHandler和DataHandler的函数实现中不能直接调用(*Connection).Close,否则会导致死锁。
type Connection
func NewConnection(conn Socket, maxcount int, dh DataHandler, ih IdentityHandler, eh ErrorHandler) *Connection
func (p *Connection) Start()
func (p *Connection) Close()
func (p *Connection) Query(data []byte) (res []byte, err error)
func (p *Connection) Reply(query, answer []byte) error
func (p *Connection) Write(data []byte) errortype Socket interface {Read() ([]byte, error)Write([]byte) errorClose()
}type DataHandler interface {Process([]byte)
}type ErrorHandler interface {OnError(error)
}type IdentityHandler interface {GetIdentity([]byte) uint32SetIdentity([]byte, uint32)
}
完整的代码实现:
package multiplexerimport ("errors""sync""sync/atomic"
)var (ERR_EXIT = errors.New("exit")
)type Socket interface {Read() ([]byte, error)Write([]byte) errorClose()
}
type DataHandler interface {Process([]byte)
}
type ErrorHandler interface {OnError(error)
}
type IdentityHandler interface {GetIdentity([]byte) uint32SetIdentity([]byte, uint32)
}
type Connection struct {conn Socketwg sync.WaitGroupmutex sync.Mutexapplicants map[uint32]chan []bytechexit chan boolchsend chan []bytechch chan chan []bytedh DataHandlerih IdentityHandlereh ErrorHandleridentity uint32
}func NewConnection(conn Socket, maxcount int, dh DataHandler, ih IdentityHandler, eh ErrorHandler) *Connection {count := maxcountif count < 1024 {count = 1024}chch := make(chan chan []byte, count)for i := 0; i < count; i++ {chch <- make(chan []byte, 1)}return &Connection{conn: conn,applicants: make(map[uint32]chan []byte, count),chsend: make(chan []byte, count),chexit: make(chan bool),chch: chch,dh: dh,ih: ih,eh: eh,}
}
func (p *Connection) Start() {p.wg.Add(2)go func() {defer p.wg.Done()p.recv()}()go func() {defer p.wg.Done()p.send()}()
}
func (p *Connection) Close() {close(p.chexit)p.conn.Close()p.wg.Wait()
}
func (p *Connection) Query(data []byte) (res []byte, err error) {var ch chan []byteselect {case <-p.chexit:return nil, ERR_EXITcase ch = <-p.chch:defer func() {p.chch <- ch}()}id := p.newIdentity()p.ih.SetIdentity(data, id)p.addApplicant(id, ch)defer func() {if err != nil {p.popApplicant(id)}}()if err := p.Write(data); err != nil {return nil, err}select {case <-p.chexit:return nil, ERR_EXITcase res = <-ch:break}return res, nil
}
func (p *Connection) Reply(query, answer []byte) error {// put back the identity attached to the queryid := p.ih.GetIdentity(query)p.ih.SetIdentity(answer, id)return p.Write(answer)
}
func (p *Connection) Write(data []byte) error {select {case <-p.chexit:return ERR_EXITcase p.chsend <- data:break}return nil
}
func (p *Connection) send() {for {select {case <-p.chexit:returncase data := <-p.chsend:if p.conn.Write(data) != nil {return}}}
}
func (p *Connection) recv() (err error) {defer func() {if err != nil {select {case <-p.chexit:err = nildefault:p.eh.OnError(err)}}}()for {select {case <-p.chexit:return nildefault:break}data, err := p.conn.Read()if err != nil {return err}if id := p.ih.GetIdentity(data); id > 0 {ch, ok := p.popApplicant(id)if ok {ch <- datacontinue}}p.dh.Process(data)}return nil
}
func (p *Connection) newIdentity() uint32 {return atomic.AddUint32(&p.identity, 1)
}
func (p *Connection) addApplicant(identity uint32, ch chan []byte) {p.mutex.Lock()defer p.mutex.Unlock()p.applicants[identity] = ch
}
func (p *Connection) popApplicant(identity uint32) (chan []byte, bool) {p.mutex.Lock()defer p.mutex.Unlock()ch, ok := p.applicants[identity]if !ok {return nil, false}delete(p.applicants, identity)return ch, true
}
本文来自:CSDN博客
感谢作者:abv123456789
查看原文:[Golang]网络游戏协议处理框架
Golang网络游戏协议处理框架相关推荐
- Golang实现简单爬虫框架(4)——队列实现并发任务调度
前言 在上一篇文章<Golang实现简单爬虫框架(3)--简单并发版>中我们实现了一个最简单并发爬虫,调度器为每一个Request创建一个goroutine,每个goroutine往Wor ...
- Golang实现简单爬虫框架(5)——项目重构与数据存储
前言 在上一篇文章<Golang实现简单爬虫框架(4)--队列实现并发任务调度>中,我们使用用队列实现了任务调度,接下来首先对两种并发方式做一个同构,使代码统一.然后添加数据存储模块. 注 ...
- 基于《悉尼协议》框架下Java课程案例教学研究
文章目录 基于<悉尼协议>框架下Java课程案例教学研究 一.Java课程教学存在问题 (一)Java课程目标定位不足 (二)Java课程教学存在的问题 1. 教材内容更新滞后 2. 学习 ...
- Go实战--golang中使用echo框架中JSONP(labstack/echo)
生命不止,继续 go go go !!! 继续,echo web框架,今天就聊一聊JSONP. JSONP 1.什么是JSONP? JSONP (JSON with padding) is used ...
- 大型网络游戏服务器的框架设计(一)
服务器是用来处理高并发的请求,同时能够满足扩展的业务逻辑的需求,最重要的是满足三点:并发性,稳定性,扩展性. 经历过两款上线游戏产品,见识到了游戏行业的杂乱无章,虽然和传统软件行业相比,少了那么些规范 ...
- 一,蓝牙协议的框架图解
一,蓝牙协议的框架 直接上图,不想再去理解一遍,之前的文档做了一些,直接搬运两个图来赋上 BLE蓝牙协议的框架图 BR/EDR蓝牙的框架图:
- golang Leaf 游戏服务器框架简介
Leaf 是一个由 Go 语言(golang)编写的开发效率和执行效率并重的开源游戏服务器框架.Leaf 适用于各类游戏服务器的开发,包括 H5(HTML5)游戏服务器. Leaf 的关注点: 良好的 ...
- golang中的gin框架学习
gin框架中常用方法 gin.H{ } 有这么一行c.JSON(200, gin.H{"message": "use get method"}) 这其中有一个g ...
- 浅谈《帧同步网络游戏》之“框架”实现思路
版权申明: 本文为"优梦创客"原创文章,您可以自由转载,但必须加入完整的版权声明 更多学习资源请加QQ:1517069595获取(企业级性能优化/热更新/Shader特效/服务器/ ...
最新文章
- MicroPython支持图形化编辑了:Python Editor带你轻松玩转MicroPython
- 2020 操作系统第一天复习(习题总结)
- mybatis mapper文件找不到_MyBatis 面试题
- 自动驾驶中的滞后碰撞检测(lazy-collision-checking)
- win7怎么跳过硬盘自检_DiskGenius 5.2.1发布!增强扇区复制功能,方便坏道硬盘做镜像!...
- python3-字符串常用操作
- Win7 下安装ubuntu14.04双系统
- Struts2 初探
- 一位阿里云小哥要感谢“双11”,于是说了一段脱口秀……
- 【面试题】-java分布式及微服务面试题汇总
- 8421BCD码与十进制之间的转换
- 如何增加无人机的飞行时间和升力?
- c语言用乘法,c语言口诀(用c语言编写乘法口诀)
- wincc做皮带动画_wincc 如何做动画
- 对手游渠道商的一些看法
- 数据库笔记 NO.1 ------------2020.03.26
- 用python模拟登录12306
- codeblocks 10.5配置vc2008x64编译器
- Android Manager之AssetManager
- 随笔-OC获取系统时间,获取绝对时间,获得真实时间
热门文章
- java http服务_springboot官方例子中文翻译--RESTful服务启用CORS支持
- html标签学习日记之(表格table)
- e.target+addEventListener事件委托
- 想为自己设置的软件加一个属于自己的图标吗?使用AWT_Swing_图标解决你的问题(源码解析)
- java 圆括号,Java圆括号翻转字符串
- C# WebService获取天气信息
- mysqll索引实验
- linux 下的动态库制作 以及在python 中如何调用 c 函数库
- 中间件配置文件-nginx
- 【李宏毅2020 ML/DL】P24 Semi-supervised