V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
chrislon
V2EX  ›  问与答

Go 游戏服务器入门文档:如何构建你的第一个 nano 应用

  •  
  •   chrislon · 2017-08-15 21:19:04 +08:00 · 3675 次点击
    这是一个创建于 2660 天前的主题,其中的信息可能已经有所发展或是发生改变。

    https://github.com/lonnng/nano/blob/master/docs/get_started_zh_CN.md

    --- 文档复制于 2017-08-18,最新内容请参考最新地址----

    如何构建你的第一个nano应用

    在这个教程中,我们将构建一个基于浏览器和WebSocket的聊天应用。

    由于游戏在场景管理、客户端动画等方面有一定的复杂性,并不适合作为nano的入门应用。对于大多数开发者 而言,普通聊天室是一个更加适合入门nano的应用。

    nano是一个轻量级的服务器框架,它最适合的应用领域是网页游戏、社交游戏、移动游戏的服务端。当然还不 仅仅是游戏,用nano开发高实时 web 应用也非常合适。

    前言

    • 本教程适用于对nano零基础的用户,如果你已经有过一定的nano开发基础,请跳过这个教程,你可以阅读 开发指南,那里会对一些话题作较为详细的探讨。

    • 由于nano是基于 Go 开发的,因此希望你在阅读本教程前对 Go 语言有一些了解。

    • 本教程的示例源码放在 github 上完整代码

    • 本教程将以一个实时聊天应用为例子,通过对这个应用进行不同的修改来展示nano框架的一些功能特性,让用 户能大致了解nano,熟悉并能够使用nano进行应用程序的开发。

    • 本教程假定你使用的开发环境是类 Unix 系统,如果你使用的 Windows 系统,希望你能够知道相关的对应方式,比 如一些.sh 脚本,在 Windows 下会使用一个同名的 bat 文件,本教程中对于 Windows 系统,不做特殊说明。

    术语解释

    nano有一些自己的术语,这里先对术语做一些简单的解释,给读者一个直观的概念,不至于看到相应术语时产生 迷惑。

    组件(Component)

    nano应用是由一些松散耦合的Component组成的,每个Component完成一些功能。整个应用可以看作是一 个Component容器,完成Component的加载以及生命周期管理。每个Component往往有InitAfterInitBeforeShutdownShutdown等方法,用来完成生命周期管理。

    type DemoComponent struct{}
    
    func (c *DemoComponent) Init()           {}
    func (c *DemoComponent) AfterInit()      {}
    func (c *DemoComponent) BeforeShutdown() {}
    func (c *DemoComponent) Shutdown()       {}
    

    Handler

    Handler用来处理业务逻辑,Handler可以有如下形式的签名:

    // 以下的 Handler 会自动将消息反序列化,在调用时当做参数传进来
    func (c *DemoComponent) DemoHandler(s *session.Session, payload *pb.DemoPayload) error {
        // 业务逻辑开始
        // ...
        // 业务逻辑结束
    
        return nil
    }
    
    // 以下的 Handler 不会自动将消息反序列化,会将客户端发送过来的消息直接当作参数传进来
    func (c *DemoComponent) DemoHandler(s *session.Session, raw []byte) error {
        // 业务逻辑开始
        // ...
        // 业务逻辑结束
    
        return nil
    }
    

    路由(Route)

    route 用来标识一个具体服务或者客户端接受服务端推送消息的位置,对服务端来说,其形式一般是..,例如 "Room.Message", 在我们的示例中, Room是一个包含相关Handler的组件, Message是一个定义在 Room中的Handler, Room中所有符合Handler签名的方法都会在nano应用启动时自动注册.

    对客户端来说,其路由一般形式为 onXXX(比如我们示例中的 onMessage),当服务端推送消息时,客户端会 有相应的回调。

    会话(Session)

    Session对应于一个客户端会话, 当客户端连接服务器后, 会建立一个会话, 会话在玩家保持连接期间可以 用于保存一些上下文信息, 这些信息会在连接断开后释放.

    组(Group)

    Group可以看作是一个Session的容器,主要用于需要广播推送消息的场景。可以把某个玩家的Session加 入到一个Group中,当对这个Group推送消息的时候,所有加入到这个Group的玩家都会收到推送过来的消 息。一个玩家的Session可能会被加入到多个Group中,这样玩家就会收到其加入的Group推送过来的消息。

    请求(Request), 响应(Response), 通知(Notify), 推送(Push)

    nano中有四种消息类型的消息,分别是请求(Request), 响应(Response), 通知(Notify)和推送(Push),客 户端发起Request到服务器端,服务器端处理后会给其返回响应Response; Notify是客户端发给服务端的 通知,也就是不需要服务端给予回复的请求; Push是服务端主动给客户端推送消息的类型。在后面的叙述中,将 会使用这些术语而不再作解释。

    示例开始

    Server

    package main
    
    import (
    	"fmt"
    	"log"
    	"net/http"
    
    	"github.com/lonnng/nano"
    	"github.com/lonnng/nano/component"
    	"github.com/lonnng/nano/serialize/json"
    	"github.com/lonnng/nano/session"
    )
    
    type (
    	// define component
    	Room struct {
    		component.Base
    		group *nano.Group
    	}
    
    	// protocol messages
    	UserMessage struct {
    		Name    string `json:"name"`
    		Content string `json:"content"`
    	}
    
    	NewUser struct {
    		Content string `json:"content"`
    	}
    
    	AllMembers struct {
    		Members []int64 `json:"members"`
    	}
    
    	JoinResponse struct {
    		Code   int    `json:"code"`
    		Result string `json:"result"`
    	}
    )
    
    func NewRoom() *Room {
    	return &Room{
    		group: nano.NewGroup("room"),
    	}
    }
    
    func (r *Room) AfterInit() {
    	nano.OnSessionClosed(func(s *session.Session) {
    		r.group.Leave(s)
    	})
    }
    
    // Join room
    func (r *Room) Join(s *session.Session, msg []byte) error {
    	s.Bind(s.ID()) // binding session uid
    	s.Push("onMembers", &AllMembers{Members: r.group.Members()})
    	// notify others
    	r.group.Broadcast("onNewUser", &NewUser{Content: fmt.Sprintf("New user: %d", s.ID())})
    	// new user join group
    	r.group.Add(s) // add session to group
    	return s.Response(&JoinResponse{Result: "sucess"})
    }
    
    // Send message
    func (r *Room) Message(s *session.Session, msg *UserMessage) error {
    	return r.group.Broadcast("onMessage", msg)
    }
    
    func main() {
    	nano.Register(NewRoom())
    	nano.SetSerializer(json.NewSerializer())
    	nano.EnableDebug()
    	log.SetFlags(log.LstdFlags | log.Llongfile)
    
    	http.Handle("/web/", http.StripPrefix("/web/", http.FileServer( http.Dir("web"))))
    
    	nano.SetCheckOriginFunc(func(_ *http.Request) bool { return true })
    	nano.ListenWS(":3250")
    }
    
    1. 首先, 导入这个代码片段需要应用的包
    2. 定义Room组件
    3. 定义所有全后端交互可能用到的协议结构体(实际项目中可能使用 Protobuf)
    4. 定义所有的 Handler, 这里包含JoinMessage
    5. 启动我们的应用
      • 注册组件
      • 设置序列化反序列器
      • 开启调试信息
      • 设置 log 输出信息
      • Set WebSocket check origin function
      • 开始监听WebSocket地址":3250"

    Client

    参考各个客户端 SDK 文档

    Summary

    这部分, 我们构建了一个简单的聊天应用, 并对代码做了简单的介绍, 通过这个教程, 相信读者对nano的工作 流程和工作机制有了一个初步的了解.

    目前尚无回复
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3079 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 00:35 · PVG 08:35 · LAX 16:35 · JFK 19:35
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.