网站设计公司有用吗,创业网站平台排名,为什么就一个网站打不开,美食网站建设多少钱1 Program Examples Overview
用easy 实现的 服务端 和客户端样例。
simple 项目构建了比较完备的目录结构#xff0c;可以作为空项目拿到项目中直接应用。 传送门#xff1a;https://github.com/slclub/easy
位置#xff1a; examples/simple examples/simple_cl…1 Program Examples Overview
用easy 实现的 服务端 和客户端样例。
simple 项目构建了比较完备的目录结构可以作为空项目拿到项目中直接应用。 传送门https://github.com/slclub/easy
位置 examples/simple examples/simple_client 2 Simple
比较简单的源码样例
这是一个简单的服务端你可以直接拿它做项目扩展开发即可。
最基本的easy框架使用简单的游戏架构不包含数据层simple代码以极简化为主项目扩展要 结构化一些目录多是空的
2.1 simple.Server
项目名simple
目录结构
-conf // 配置
--controller 控制器也就是解读消息的入口--callback 放置一些基本的回调函数如链接创建服务平滑关闭等--login 登陆模块--player 用户玩家--store 商铺--world 大世界相关
--initialize 初始化工程启动执行一次与运行时无关
--lservers 接入easy监听服务 l 是 listen 当让也可以接入其他的监听服务
--message 消息定义
--models 数据模型尽量只有数据结构的定义和基本验证
--services 游戏逻辑存放区域主要的逻辑都可以放在这里
--vendors 您项目的一些必要基础功能性的包或者接入第三方包且这个包需要配置等// 并非是替代 go mod注意go mod 中也有一个verdor 且会产生vendor文件夹我们这里的vendors 仅仅是common 通用基础标准等的意思这里的包之间互相依赖也少或者说机会是无比较大功能性的包引入后总需要配置一些东西甚至和自己的配置参数相关那么放在这里改造一下符合工程写法结构要求等就比较合适了
运行命令go build ./simple 2.2 simple.Client
项目名simple_client
运行命令go build ./simple_client
测试simple服务端对应的客户端样例工程 3 项目代码
3.1 main
在main中Start() 中easy.Serv 可以传入多个服务组件。lservers.Server1()监听的是websocketlservers.Server2()监听的是TCP。一个应用服务程序可以容易的监听多个端口服务且任意多个多个同一类型的协议也是支持的。
我们在easy.Serv统一阻断主goroutine其他可以直接使用内置的go 方法直接调用没有任何高级花哨的调用方式。 import (github.com/slclub/easysimple/initializesimple/lservers
)func main() {initialize.Init()Start()
}func Start() {easy.Serv(lservers.Server1(), // websocket 监听服务 可以有多个lservers.Server2(), // tcp 服务)
} 3.2 lservers(listening service)
监听端口的服务代码也很少这里样例代码就没有按服务分文件。直接上源码
func Server1() servers.ListenServer {return server1
}func Server2() servers.ListenServer {return server2
}func InitListenServer() {server1 servers.NewWSServer()server1.Init(option.OptionWith(agent.Gate{Addr: :18080,Protocol: typehandle.ENCRIPT_DATA_JSON,PendingWriteNum: 2000,LittleEndian: true,MaxConnNum: 2000,}).Default(option.DEFAULT_IGNORE_ZERO))server2 servers.NewTCPServer()server2.Init(option.OptionWith(agent.Gate{Addr: :18081,Protocol: typehandle.ENCRIPT_DATA_JSON,PendingWriteNum: 2000,LittleEndian: true,MaxConnNum: 2000,}).Default(option.DEFAULT_IGNORE_ZERO))
}
servers 是easy的监听服务基础包packageserver1 :servers.NewWSServer() 是new一个websocket 服务server1.Init()初始化option.OptionWith 是我们的一个开放配置选择包为了易用和方便配置方式多样默认值等而开发。
参数 Addr监听地址 Protocol选用编码组件快捷换编码的方式换自定义编码组件在后续章节会说明 PendingWriteNum排队消息长度 LittleEndiantrue小端false大端 MaxConnNum最大链接数 option.DEFAULT_IGNORE_ZERO: 如果赋值0值或者没有给相应字段赋值则使用默认值。其中Default方法等于use类似中间件 3.3 controller
handle
控制器层面MVC中的C接收消息的下一步就是它了。用controller/login模块举例
import (github.com/slclub/easy/nets/agentreflectsimple/vendors/log8q
)func HandleLogin(agent1 agent.Agent, arg any) {log8q.Log().Info(WS controller.Handle.Login info: , reflect.TypeOf(arg).Elem().Name())
}func HandleLoginTcp(agent2 agent.Agent, arg any) {log8q.Log().Info(TCP controller.Handle.Login info: , reflect.TypeOf(arg).Elem().Name())
}分别是websocket 和Tcp 的login handle它们做的事情是一样。写handle接收消息就是这样简单。
agent.Agent 理解成连接可以绑定到你的对象上业务逻辑所用的handle你也可以再次封装下函数签名类似HandleXXX(player *Player, msg Any) 。arg any 是接收客户端的消息我们直接粗暴的用reflect查出它的结构体名字以做测试验证。
binding
做完handle需要将它与消息以及监听的服务绑定绑定方法也很简单。直接上代码 import (github.com/slclub/easy/typehandlesimple/controller/loginsimple/lserverssimple/message/IDsimple/message/json
)func InitBindingRoute() {r1 : lservers.Server1().Router()r1.Register(ID.LOGIN_REQ, json.LoginReq{}, typehandle.HandleMessage(login.HandleLogin))
}func InitBindingRouteServer2() {r2 : lservers.Server2().Router()r2.Register(ID.LOGIN_REQ, json.LoginReq{}, typehandle.HandleMessage(login.HandleLoginTcp))
}
直接使用监听服务的Router() 获取路由使用路由Register()绑定 消息ID消息体和消息handle其中handle是可选的response返回给客户端的消息是不需要handle的。 这样哪个监听服务对应哪个handle也是一目了然。
callback
这仅仅是笔者自己起的模块名字目的是为了给连接Open和Close做监听回调的handle。与业务handle有点不同少了个消息参数。 import (github.com/slclub/easy/nets/agentgithub.com/slclub/easy/serverssimple/lserverssimple/vendors/log8q
)func RegisterCallerToLservers() {lservers.Server1().Hook().Append(servers.CONST_AGENT_NEW, handleOnConnNew)lservers.Server1().Hook().Append(servers.CONST_AGENT_CLOSE, handleOnConnClose)lservers.Server1().Hook().Append(servers.CONST_SERVER_CLOSE, handleOnServerClose)lservers.Server2().Hook().Append(servers.CONST_AGENT_NEW, handleOnConnNew)lservers.Server2().Hook().Append(servers.CONST_AGENT_CLOSE, handleOnConnClose)
}func handleOnConnNew(ag agent.Agent) {log8q.Log().Info([CONNECTION.NEW] server create an new connection)
}func handleOnConnClose(ag agent.Agent) {log8q.Log().Info([CONNECTION.CLOSE] server closed an old connection)
}// the current listening server is closing
// smoothly shutdown the server
func handleOnServerClose(ag agent.Agent) {// ag nil// 执行一些 平滑停服务的逻辑
}同样需要我们用具体的监听服务去调用钩子对象去Append添加链接的handle。需要注意我们使用的是Append一个钩子可以添加多个handle。笔者相信为了性能大多数人仅仅会用一个来完善自己链接在线逻辑。
长链接的监听服务可以共用此handle。
其他的controller
就是我们按照逻辑流程划分的业务模块不再赘述了。 4 vendors
笔者把一些引用第三方需要我们简单封装或配置等的或者自己实现单独功能依赖少的包可以放在vendors 下面作为third package存放之地。这个目录名不要用vendor它是go默认使用vendor这以前是一套项目部署方案有了go mod 它就不香了。后来的很多人甚至没见过它。记得不要混淆vendors 和vendor。
4.1 log8q
笔者使用了自己写的log8q这个日志库zip虽然性能高且强大总有些记不住特别依赖goland等IDE才好用一些。
笔者按照log4j的思想撸了个日志的轮子可配置级别支持官方的log接口也有自己简单的使用InfoDebugWarnErrorFatal等级别每一个传参数的方式都与fmt.Println类似。可以设置日志保留时间30 * 8640030天。
主打一个简单易用组件化性能够用就好。 5 Message 消息
由于go的import特性等建议将消息定义为单独一个package减少loopimport的概率。
在message/register.go 中也是注册消息是不是熟悉在controller中我们也有消息注册绑定其实全放在controller里注册也是可以的在这里我们主要就是注册哪些没有handle的消息 import (simple/lserverssimple/message/IDsimple/message/json
)// 将不需要handle 处理的消息 尽量放在这里注册
// 可以将所有注册消息都放在这里也可以
func Init() {InitJson()InitProtobuf()
}func InitJson() {r1 : lservers.Server1().Router()r1.Register(ID.LOGIN_RES, json.LoginRes{}, nil)r2 : lservers.Server2().Router()r2.Register(ID.LOGIN_RES, json.LoginRes{}, nil)
}func InitProtobuf() {//r2 : lservers.SimpleServ1.Router()
}6 总结
致于其它的目录结构也没什么内容介绍目录的结构的tree中说明就足够了。你有自己习惯可以改吗不是硬性要求。整体看下来代码量很少的吧。
一个完整的单机游戏工程就构建完毕了。数据库缓存等就用gorm等即可。
致于说单线程开发在golang中使用 go 和channel 可以轻易的实现安全稳定的goroutine。并不需要我们过多的给予定式封装反而难用且性能低下。不同的需求用不同的方式去控制线程就好了。
go是一个高并发语言开携程像吃饭喝水一样简单控制好携程数量可能稍有难度。所以我们不要被单线程思想限制住并发控制好共有资源代码也不见的就一定复杂一定多。性能强力资源占用少开发方便稳定性高就好了服务器也就这点追求了。当然你能让你的代码变现金那是比较实在的最求出发点不太一样是吧。 后期我会发布Aoi的package单线程的且与handle多线程互通有无。位置 EASY.vendors/aoi。