『网络游戏』GoLand服务器框架【01】

打开GoLand创建项目

编写Go程序:main.go

package mainimport ("fmt""newgame/game/gate""os""os/signal""syscall""time"
)var (SinChan   = make(chan os.Signal, 1)closeChan chan struct{}
)func main() {//信号通道,用于接收系统信号signal.Notify(SinChan, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)//逻辑更新定时器,每66毫秒更新一次逻辑logicTicker := time.NewTicker(66 * time.Millisecond)//关闭通道,用于通知程序退出closeChan := make(chan struct{})//退出通道,用于通知程序退出完成exitChan := make(chan struct{})go func() {defer func() {exitChan <- struct{}{}}()//程序初始化地方,可以初始化一些全局变量,或者启动一些协程gs := gate.NewGate()gs.Run()//程序大的循环,处理信号,逻辑更新。。。for {select {//接收到关闭通道,退出程序case <-closeChan:goto QUIT//接受到系统信号,处理信号case sig := <-SinChan:fmt.Println("receive signal:", sig)close(closeChan)case <-logicTicker.C://逻辑更新//fmt.Println("logic update")}}QUIT://处理程序退出逻辑return}()//等待程序退出<-exitChan
}

编写Go程序:server.go

package networkimport ("net""sync"
)// Server网络服务器
type Server struct {callback  ConnCallback  //每一个连接的回调protocol  Protocol      //通用协议,用于解析网络协议,处理粘包问题,可以自定义exitChan  chan struct{} //退出信号,用于通知服务器退出waitGroup *sync.WaitGroupcloseOnce sync.Oncelistener  net.Listener //监听器
}// newServer 创建一个网络服务器
func NewServer(callback ConnCallback, protocol Protocol) *Server {return &Server{callback:  callback,protocol:  protocol,exitChan:  make(chan struct{}),waitGroup: &sync.WaitGroup{},}
}type ConnectionCreator func(conn net.Conn, src *Server) *Connection// start 启动服务器
func (s *Server) Start(listener net.Listener, create ConnectionCreator) {s.listener = listeners.waitGroup.Add(1)defer func() {s.waitGroup.Done()}()for {select {case <-s.exitChan:returndefault:}conn, err := listener.Accept()if err != nil {break}s.waitGroup.Add(1)go func() {create(conn, s).Do()s.waitGroup.Done()}()}
}// Stop停止服务器
func (s *Server) Stop(wait bool) {s.closeOnce.Do(func() {close(s.exitChan)s.listener.Close()})if wait {s.waitGroup.Wait()}
}

编写Go程序:protocol.go

package networkimport ("encoding/binary""errors""io"
)// 网络消息序列化接口
type Packet interface {Serialize() []byte
}// 网络协议读取的接口
type Protocol interface {ReadPacket(conn io.Reader) (Packet, error)
}// 程序默认协议结构
type DefaultPacket struct {buff []byte
}// 实现Packet接口
func (dp *DefaultPacket) Serialize() []byte {return dp.buff
}// 获取消息体的二进制数据
func (db *DefaultPacket) GetBody() []byte {return db.buff[4:]
}// 创建一个默认协议的消息包
func NewDefaultPacket(buff []byte) *DefaultPacket {p := &DefaultPacket{}p.buff = make([]byte, 4+len(buff))binary.BigEndian.PutUint32(p.buff[:4], uint32(len(buff)))//拷贝消息体copy(p.buff[4:], buff)return p
}// 默认协议解析
type DefaultProtocol struct {
}// 实现接口
func (dp *DefaultProtocol) ReadPacket(conn io.Reader) (Packet, error) {var (lengthBytes []byte = make([]byte, 4)length      uint32)//读取消息长度if _, err := io.ReadFull(conn, lengthBytes); err != nil {return nil, err}if length = binary.BigEndian.Uint32(lengthBytes); length > 1024 {return nil, errors.New("the size of packet is too large")}//读取消息体buff := make([]byte, length)if _, err := io.ReadFull(conn, buff); err != nil {return nil, err}return NewDefaultPacket(buff), nil
}

编写Go程序:protocol.go

package networkimport ("errors""net""sync""sync/atomic""time"
)// 定义错误
var (ErrConnClosing   = errors.New("Connection is closing")ErrWriteBlocking = errors.New("write packet is blocking")ErrReadBlocking  = errors.New("read packet is blocking")
)type Connection struct {srv               *Serverconn              net.Conn      //原始链接extraData         interface{}   //额外的数据closeOnce         sync.Once     //关闭链接closeFlag         int32         //关闭标志closeChan         chan struct{} //关闭的信号packetSendChan    chan Packet   //发送消息的通道callback          ConnCallback  //回调的接口packetReceiveChan chan Packet   //接收消息的通道fd                uint64        //链接的唯一标识
}// ConnCallback 每一个网络链接回调的接口
type ConnCallback interface {//OnConnect 当有新的链接进来时的回调,true为接收,false拒绝OnConnect(*Connection) bool//OnMessage当读取到一个完整地游戏逻辑消息时被调用,true继续处理,false关闭OnMessage(*Connection, Packet) boolOnClose(*Connection)
}func NewConnection(conn net.Conn, srv *Server) *Connection {c := &Connection{srv:               srv,callback:          srv.callback,conn:              conn,closeChan:         make(chan struct{}),packetSendChan:    make(chan Packet, 100),packetReceiveChan: make(chan Packet, 100),}if s, ok := conn.(*net.TCPConn); !ok {panic("conn is not")} else {c.fd = uint64(s.RemoteAddr().(*net.TCPAddr).Port)}return c
}// GetFd 获取连接的唯一标识
func (c *Connection) GetFd() uint64 {return c.fd
}// close关闭连接
func (c *Connection) Close() {c.closeOnce.Do(func() {atomic.StoreInt32(&c.closeFlag, 1)close(c.closeChan)close(c.packetSendChan)close(c.packetReceiveChan)c.conn.Close()c.callback.OnClose(c)})
}// 判断链接是否关闭
func (c *Connection) IsClosed() bool {return atomic.LoadInt32(&c.closeFlag) == 1
}// 设置连接的回调
func (c *Connection) SetCallback(callback ConnCallback) {c.callback = callback
}// / AsyncWritePacket 异步写入一个数据包,如果超时则返回错误
func (c *Connection) AsyncWritePacket(p Packet, timeout time.Duration) (err error) {if c.IsClosed() {return ErrConnClosing}defer func() {if e := recover(); e != nil {err = ErrWriteBlocking}}()if timeout == 0 {select {case c.packetSendChan <- p:return nildefault:return ErrWriteBlocking}} else {select {case c.packetSendChan <- p:return nilcase <-time.After(timeout):return ErrWriteBlockingcase <-c.closeChan:return ErrConnClosing}}
}
func (c *Connection) Do() {if !c.callback.OnConnect(c) {return}asyncDo(c.handLoop, c.srv.waitGroup)asyncDo(c.readLoop, c.srv.waitGroup)asyncDo(c.writeLoop, c.srv.waitGroup)
}
func asyncDo(fn func(), wg *sync.WaitGroup) {wg.Add(1)go func() {fn()wg.Done()}()
}
func (c *Connection) readLoop() {defer func() {recover()c.Close()}()for {select {case <-c.srv.exitChan:returncase <-c.closeChan:returndefault:}c.conn.SetReadDeadline(time.Now().Add(time.Second * 180))p, err := c.srv.protocol.ReadPacket(c.conn)if err != nil {return}c.packetReceiveChan <- p}
}// 写数据包到链接
func (c *Connection) writeLoop() {defer func() {recover()c.Close()}()for {select {case <-c.srv.exitChan:returncase <-c.closeChan:returncase p := <-c.packetSendChan:if c.IsClosed() {return}c.conn.SetWriteDeadline(time.Now().Add(time.Second * 180))if _, err := c.conn.Write(p.Serialize()); err != nil {return}}}
}
func (c *Connection) handLoop() {defer func() {recover()c.Close()}()for {select {case <-c.srv.exitChan:returncase <-c.closeChan:returncase p := <-c.packetReceiveChan:if c.IsClosed() {return}if !c.callback.OnMessage(c, p) {return}}}
}

编写Go程序:gate.go

package gateimport ("fmt""net""newgame/game/network"
)// 网关服务
func NewGate() *Gate {return &Gate{}}type Gate struct {listener net.Listener
}// Run启动网关服务器
func (g *Gate) Run() {l, e := net.Listen("tcp", ":10087")if e != nil {panic(e.Error())}g.listener = lfmt.Printf("Linten on %s\n", l.Addr().String())server := network.NewServer(g, &network.DefaultProtocol{})go server.Start(l, func(conn net.Conn, i *network.Server) *network.Connection {return network.NewConnection(conn, server)})
}// Stop停止网关服务器
func (g *Gate) Stop() {err := g.listener.Close()if err != nil {panic(err.Error())}
}// OnConnect 连接建立回调
func (g *Gate) OnConnect(conn *network.Connection) bool {fmt.Printf("new connection:%d\n", conn.GetFd())return true
}// OnClose 连接关闭回调
func (g *Gate) OnClose(conn *network.Connection) {}
func (g *Gate) OnMessage(conn *network.Connection, p network.Packet) bool {return true
}

安装telnet

确定安装即可

Windows + R 输入cmd 打开命令框

输入 telnet 127.0.0.1 10087

(其中10087是代码中所写)

GoLand输出显示Debug信息即服务器连接成功

其中输出的Debug信息(new connection:12345)在脚本:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1549173.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

【PyTorch入门】一文解释 PyTorch的求导 (backward、autograd.grad)

&#x1f308; 个人主页&#xff1a;十二月的猫-CSDN博客 &#x1f525; 系列专栏&#xff1a; &#x1f3c0;PyTorch入门宝典_十二月的猫的博客-CSDN博客 &#x1f4aa;&#x1f3fb; 十二月的寒冬阻挡不了春天的脚步&#xff0c;十二点的黑夜遮蔽不住黎明的曙光 目录 1. 动…

攻防世界---->happyctf

做题笔记。 下载 查壳。 32ida打开。 先运行一下&#xff1a; C写的。 追踪 good job 具体跟踪分析&#xff1a; 说白了&#xff0c;就是一个用于判断 flag key的。 往上走&#xff1a; 跟进。 打开 od吧。 锁定地址 追踪看看。&#xff08;此题&#xff0c;ida不能动态 od可以…

yolov5源码分析001

文章目录 1.研究背景2.源码位置3.源码 1.研究背景 最近项目需要将前人做的YOLOv5改造项目继续改造,于是研究其代码,一步步剖析,一步步看一个个代码意义,旨在为后期攻克YOLOv10等系列做好准备. 2.源码位置 3.源码 # 下载指定文件,并保存在指定目录文件夹中,最后返回文件完整路…

Win10系统插入带有麦克风的耳机_麦克风不起作用_解决方法_亲测成功---Windows运维工作笔记054

今天我在使用讯飞输入法的时候,想通过讯飞的语音输入法来提高自己的输入效率。 但是这个时候发现一个问题就是我插入我的台式机的是一个带有麦克风的耳机。 但是发现我这个耳机没有办法被电脑识别出麦克风来,所以说就没办法使用讯飞输入法的语音输入功能来直接输入文字了。…

Linux虚拟机安装教程

一、前期准备 1.下载VMware Workstation 官网地址&#xff1a;https://access.broadcom.com 进入后需使用邮箱注册登录&#xff0c;登陆后会进入到控制台&#xff0c;选择My Downloads 下滑找到VMware Workstation Pro 选择免费版 选择需要的版本&#xff0c;我下载的是17.5…

Java实现找色和找图功能

某天&#xff0c;张三接到一个任务需求&#xff0c;将一个Excel表格里面的员工信息&#xff0c;录入到员工系统里面&#xff0c;由于数据量非常大&#xff0c;操作起来巨慢。经过一段时间的操作和观察&#xff0c;他发现这种操作&#xff0c;非常有规律&#xff0c;基本就是一些…

SpringBoot3.X配置OAuth

背景 之前在学习OAuth2时&#xff0c;我就有一个疑惑&#xff0c;OAuth2中有太多的配置、服务类都标注了Deprecated&#xff0c;如下&#xff1a; 显然这些写法已经过时了&#xff0c;那么官方推荐的最新写法是什么样的呢&#xff1f;当时我没有深究这些&#xff0c;我以为我放…

[大语言模型-论文精读] 词性对抗性攻击:文本到图像生成的实证研究

[大语言模型-论文精读] 词性对抗性攻击&#xff1a;文本到图像生成的实证研究 目录 文章目录 [大语言模型-论文精读] 词性对抗性攻击&#xff1a;文本到图像生成的实证研究目录文章研究背景 文章标题摘要1 引言2 相关工作3 数据集创建3.1 数据收集3.2 目标提示生成3.3 数据集注…

从日志到洞察:轻松实现服务器安全管理的神器

在当今复杂多变的网络环境中&#xff0c;服务器安全管理已成为一项不可或缺的任务。然而&#xff0c;面对海量的日志数据&#xff0c;如何快速精准地提取有价值的信息&#xff0c;并及时发现潜在的安全威胁&#xff1f;本文将为您介绍一款强大的服务器日志检索与查杀工具&#…

【AHK】打造炒股利器系列——用数组和循环来简化语音报时器

上一篇文章&#xff0c;【AHK】打造炒股利器系列——语音报时器 作为AHK入门&#xff0c;讲解了 注释、赋值、if语句、逻辑运算符、定时器等基本知识。本篇将引入Array和Loop语句来简化化这个语音报时器&#xff0c;让代码更优雅&#xff0c;代码越简单越不容易出错误&#xff…

07-阿里云镜像仓库

07-阿里云镜像仓库 注册阿里云 先注册一个阿里云账号&#xff1a;https://www.aliyun.com/ 进入容器镜像服务控制台 工作台》容器》容器服务》容器镜像服务 实例列表》个人实例 仓库管理》镜像仓库》命名空间》创建命名空间 仓库管理》镜像仓库》镜像仓库》创建镜像仓库 使…

Spring Boot技术栈:打造高效在线商城

2 相关技术 2.1 Springboot框架介绍 Spring Boot是由Pivotal团队提供的全新框架&#xff0c;其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置&#xff0c;从而使开发人员不再需要定义样板化的配置。通过这种方式&#xff0c;Spring…

异步框架 fastapi -- 简单介绍

文章目录 fastapi 介绍restful接口设计简单应用Swagger风格的接口文档 fastapi 介绍 fastapi官方文档 fastapi 是现代化、高性能、基于python标准类型注释的异步web框架&#xff1b;基于python构建web APIs&#xff0c;性能可比go语言&#xff1b;高效编码&#xff0c;更少的…

Word导出样式模板,应用到其他所有word

一&#xff0c;打开自己定义好的模板word文件&#xff0c;要用docx格式 打开自己自定义好各种样式的模板的docx文件。 二&#xff0c; 另存为 -》 选一个位置 三&#xff0c;保存为dotm格式 名字要改为Normal 保存类型要用dotm格式。 四&#xff0c;去找word默认加载的…

OpenAI o1与GPT-4o究竟强在哪里

OpenAI 的 O1 模型与 GPT-4o 相比&#xff0c;具有显著的技术进步和性能提升。以下是两者的主要区别和 O1 的进步之处&#xff1a; 推理能力&#xff1a;O1 模型在处理复杂问题&#xff08;如编程和数学&#xff09;方面表现出更强的推理能力。例如&#xff0c;在国际数学奥林匹…

大模型微调方法(非常详细),收藏这一篇就够了!

引言 众所周知&#xff0c;大语言模型(LLM)正在飞速发展&#xff0c;各行业都有了自己的大模型。其中&#xff0c;大模型微调技术在此过程中起到了非常关键的作用&#xff0c;它提升了模型的生成效率和适应性&#xff0c;使其能够在多样化的应用场景中发挥更大的价值。 那么&…

如果只能保留一个复制粘贴软件,那一定是它pastemate

下载地址&#xff1a;Pastemate 在日常的工作和生活中&#xff0c;使用电脑必离不开的功能中&#xff0c;一定有复制粘贴。传统的复制粘贴方式效率不那么高&#xff0c;Windows内置的剪切板功能感觉又差那么些意思。 &#x1f9d0;对于功能和颜值都有要求的你&#xff0c;一定…

大模型推理任务Nvidia GPU选型指南

大型语言模型 (LLM)&#xff08;如 GPT-4、BERT 和其他基于 Transformer 的模型&#xff09;彻底改变了 AI 格局。这些模型需要大量计算资源来进行训练和推理。选择合适的 GPU 进行 LLM 推理可以极大地影响性能、成本效益和可扩展性。 在本文中&#xff0c;我们将探索最适合 L…

你们猜!吊打Oracle的国产数据库有哪几家?

今天闲暇之余看到某vx群聊得很火热&#xff0c;这个群聚集了国内不少数据库大咖&#xff0c;其中大家聊到国产数据库遥遥领先了。 最开始主要是一个朋友提到目前很多企业拍板的人并不懂数据库&#xff0c;甚至很多知名数据库都没听过。 然后大家就开始聊到国产数据库了&#xf…

LeetCode[中等] 17. 电话号码的字母组合

给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数字到字母的映射如下&#xff08;与电话按键相同&#xff09;。注意 1 不对应任何字母。 思路 回溯法 log&#xff1a;当前结果数组&#xff1b;level&#xff1a…