最近海涛大神分享了一个用于Unity的游戏服务器RPGDemoServer,基于.net开发.目前开始记录学习.

文章目录

  • 总体框架
    • 服务器端
    • 客户端
  • 服务器是如何开始的
    • 新建一个服务器
    • 基于事件驱动
    • 注册模块和登陆模块
  • 新建一个客户端
    • 基础模块
    • NetIO
    • 注册/登陆模块
    • NetManager
  • 开始聊天
    • 聊天失败
    • 服务端没有角色信息
    • 新建一个角色
    • 成功收到信息

总体框架

服务器端


客户端


此框架比较庞大,应用于rpg项目基本业务是足够了.

服务器通讯部分主要是上面Net包,里面有注册,登陆,背包,好友系统,副本,技能,组队等模块.
它是以线程驱动NGUI的形式进行的,新版本的unity默认是无法运行的.
但是根据我的需求,目前精简到下面就够了,有些留在后面研究:

上面有注册模块,还有聊天模块.后面也许会加入组队模块.

服务器是如何开始的

新建一个服务器

         ServerStart ss = new ServerStart(9999);ss.Decode = MessageEncoding.Decode;ss.Encode = MessageEncoding.Encode;ss.LengthDecode = LengthEncoding.Decode;ss.LengthEncode = LengthEncoding.Encode;ss.HandlerCenter =new MessageHandlerCenter();ss.Start(1234);Console.WriteLine("服务启动成功");

这看起来是一个基本的服务器实例化代码
9999 是最大连接数.
1234 代表端口号.
中间有几行看起来是加密和解密,还有消息处理的地方.

基于事件驱动

         public override void ClientClose(UserToken userToken, string error){Console.WriteLine("有客户端断开连接!");try{_mapHandler.ClientClose(userToken);}finally{_userHandler.ClientClose(userToken);_loginHandler.ClientClose(userToken);}}public override void ClientConnect(UserToken userToken){Console.WriteLine("有客户端连接进来了!");}

我很喜欢这种事件驱动.

         public override void MessageReceive(UserToken userToken, object message){SocketModel socketModel = message as SocketModel;if(socketModel!=null)switch (socketModel.Type){case Protocol.TYPE_LOGIN:_loginHandler.MessageReceive(userToken, socketModel);break;case Protocol.TYPE_USER:_userHandler.MessageReceive(userToken, socketModel);break;case Protocol.TYPE_MAP:_mapHandler.MessageReceive(userToken,socketModel);break;case Protocol.TYPE_RAID:_raidHandler.MessageReceive(userToken,socketModel);break;case Protocol.TYPE_CHAT:_chatHandler.MessageReceive(userToken,socketModel);break;}}

我不太喜欢这种以二进制的形式来通讯,这样就无法直接通过调试工具进行查看和测试.

注册模块和登陆模块

      public void MessageReceive(UserToken userToken, SocketModel message){switch (message.Command){case LoginProtocol.LOGIN_CREQ:Console.WriteLine("登陆");Login(userToken, message.GetMessage<AccountDTO>());break;case LoginProtocol.REG_CREQ:Console.WriteLine("注册");Reg(userToken, message.GetMessage<AccountDTO>());break;case LoginProtocol.OFFLINE_CREQ:ClientClose(userToken);break;}}public void ClientClose(UserToken userToken){ExecutorPool.Instance.Executor(delegate (){_accountBiz.Close(userToken);});}private void Login(UserToken userToken ,AccountDTO value){ExecutorPool.Instance.Executor(delegate(){int result = _accountBiz.AccountLogin(userToken, value.Account, value.Password);Console.WriteLine("登陆结果=" + result);Write(userToken,Protocol.TYPE_LOGIN,0,LoginProtocol.LOGIN_SRES,result);});}private void Reg(UserToken userToken ,AccountDTO value){ExecutorPool.Instance.Executor(delegate (){int result = _accountBiz.CreateAccount(userToken, value.Account, value.Password);Console.WriteLine("注册结果="+ result);Write(userToken, Protocol.TYPE_LOGIN, 0, LoginProtocol.REG_SRES, result);});}

里面是怎么判断的 ,就不进去看了,目前只要结果就行了.

新建一个客户端

基础模块

我新建了一个unity项目来尝试和服务端进行通讯测试,基本的项目如下:


global 是全局项目,用来存储一些信息.
NetManager是用来跟服务器通讯的工具.我加入了登陆/注册,聊天模块.

NetIO

主要socket代码都在里面,应该是在NetManagerUtil 的update 里被激活的.如果没有连上服务器,貌似就在假死状态,所以这里可以做下调整.

注册/登陆模块

using UnityEngine;
using System.Collections;
using GameProtocol;
using GameProtocol.Auto;public class LoginHandler : MonoBehaviour {public delegate void loginEventHandler(int ms);public event loginEventHandler LoginEvent;public delegate void RegEventHandler(int ms);public event RegEventHandler RegEvent;public void MessageReceive(SocketModel socketModel){switch (socketModel.Command){case 0:Debug.Log("登陆信息");LoginAccount(socketModel.GetMessage<int>());break;case 2:Debug.Log("注册信息");RegAccount(socketModel.GetMessage<int>());break;} }private void RegAccount(int result){switch (result){case -1:Debug.Log("账号重复!");if (RegEvent != null){RegEvent(0);}break; case 0:if (RegEvent != null){RegEvent(1);}Debug.Log("注册成功!");break;    }}private void LoginAccount(int result){switch (result){case 0:Global.Instance.IsOnline = true;Debug.Log("登陆成功");if(LoginEvent!=null){LoginEvent (1);}break;case -1:if (LoginEvent != null){LoginEvent(0);}Debug.Log("账号不存在!");break;   case -2:if (LoginEvent != null){LoginEvent(2);}Debug.Log("账号在线!");break;   case -3:if (LoginEvent != null){LoginEvent(3);}Debug.Log("账号或密码错误!");break;   case -4:if (LoginEvent != null){LoginEvent(4);}Debug.Log("账号密码为空!");break;    }}
}

我给它加了注册事件还有登陆事件.

NetManager

using UnityEngine;
using System.Collections;
using GameProtocol;
using GameProtocol.Auto;public class NetManagerUtil : MonoBehaviour
{private LoginHandler _loginHandler;private ChatHandler _chatHandler;void Start(){DontDestroyOnLoad(this.gameObject);_loginHandler = this.GetComponent<LoginHandler>();_loginHandler.LoginEvent += handleLoginEvent;_loginHandler.RegEvent += handleRegEvent;_chatHandler = this.GetComponent<ChatHandler>();zhuce();}private void handleLoginEvent(int result){Debug.Log("登陆结果="+result);}private void handleRegEvent(int result){Debug.Log("注册结果=" + result);if(result==1){login();}}void zhuce(){// yield return new WaitForSeconds(2f);AccountDTO accountDto = new AccountDTO();accountDto.Account = "abc";accountDto.Password = "123";this.Write(Protocol.TYPE_LOGIN, 0, LoginProtocol.REG_CREQ, accountDto);Global.Instance.Account = "abc";Global.Instance.Password = "123";}void login(){AccountDTO accountDto = new AccountDTO();accountDto.Account = Global.Instance.Account;accountDto.Password = Global.Instance.Password;this.Write(Protocol.TYPE_LOGIN, 0, LoginProtocol.LOGIN_CREQ, accountDto);}void Update(){while (NetIO.Instance.MessageList.Count > 0){SocketModel socketModel = NetIO.Instance.MessageList[0];StartCoroutine("MessageReceive", socketModel);NetIO.Instance.MessageList.RemoveAt(0);}}private void MessageReceive(SocketModel socketModel){switch (socketModel.Type){case 0:_loginHandler.MessageReceive(socketModel);break;case 1:break;case 2:break;case 3:break;case 4:_chatHandler.MessageReceive(socketModel);break;}}
}

目前加入了自动注册和登陆,现在看来一切结果都在预期之中.

开始聊天

聊天失败

我先在登陆结果的地方直接输入聊天内容,服务器是收到了,但是客户端并没有收到

 private void handleLoginEvent(int result){Debug.Log("登陆结果="+result);if (result == 1){ChatDTO dto = new ChatDTO(Global.Instance.UserDto, "世上没有一件工作不辛苦,没有一处人事不复杂。不要随意发脾气,谁都不欠你的。学会低调,取舍间必有得失,不用太计较。学着踏实而务实,越努力越幸运。当一个人有了足够的内涵和物质做后盾,人生就会变得底气十足。若是美好,叫做精彩,若是糟糕,叫做经历。——互勉", ChatType.System, null);this.Write(Protocol.TYPE_CHAT, 0, ChatProtocol.CHAT_TALK_CREQ, dto);}}

以下是聊天模块:

 public void MessageReceive(SocketModel socketModel){switch (socketModel.Command){case 1:break;case 2:ChatDTO dto = socketModel.GetMessage<ChatDTO>();switch (dto.Type){case ChatType.System:SystemChatList.Add(dto);Debug.Log("System="+dto.PlayerDto.Name+":"+ dto.ChatValue);break;case ChatType.Team:string text = string.Format("[ff0000]【队伍】[-] [ffff00]{0}[-] :{1}", dto.PlayerDto.Name,dto.ChatValue);Debug.Log("Team=" + dto.PlayerDto.Name + ":" + dto.ChatValue);/* if (BottomEnumPanel.Instance != null){BottomEnumPanel.Instance.TextList.Clear();BottomEnumPanel.Instance.TextList.Add(text);}*/TeamChatList.Add(dto);// CreateChatItem(1, dto);break;case ChatType.World:string value = string.Format("[ff0000]【世界】[-] [ffff00]{0}[-] :{1}", dto.PlayerDto.Name,dto.ChatValue);/* if (BottomEnumPanel.Instance != null){BottomEnumPanel.Instance.TextList.Clear();BottomEnumPanel.Instance.TextList.Add(value);}*/WorldChatList.Add(dto);//CreateChatItem(0,dto);break;   }AllChatList.Add(dto);break;    }}

以上并没有被触发.

服务端没有角色信息

 private void Talk(UserToken userToken , ChatDTO dto){switch (dto.Type){case ChatType.World:Console.WriteLine("世界频道");break;case ChatType.System:int[] playerIds = _userBiz.GetAllOnlinePlayerId(userToken);Console.WriteLine("系统频道");Console.WriteLine("playerIds length="+ playerIds.Length);for (int i=0;i<playerIds.Length;i++){Console.WriteLine("id="+playerIds[i]);}this.Write(playerIds, Protocol.TYPE_CHAT,0,ChatProtocol.CHAT_TALK_BRO, dto);break;case ChatType.Team:Console.WriteLine("组队频道");this.Write(dto.TeamId, Protocol.TYPE_CHAT, 0, ChatProtocol.CHAT_TALK_BRO, dto);break;}Console.WriteLine("文字内容="+dto.ChatValue);}

检查这里,原来playerIds length=0;

新建一个角色

应该是因为没有角色,所以才没有办法发给客户端.那还是加入角色模块吧,先新建一个:

 private void addUser(){UserDTO userDto = new UserDTO();userDto.Name = "战士";userDto.JobId = 0;this.Write(Protocol.TYPE_USER, 0, UserProtocol.Create_CREQ, userDto);}private void handleLoginEvent(int result){Debug.Log("登陆结果="+result);if (result == 1){addUser();}}

UserHandler下改成这样:

...Debug.Log("socketModel.Command="+ socketModel.Command);switch (socketModel.Command){case 1://创建角色Debug.Log("创建角色");Create(socketModel.GetMessage<bool>());break;case 3://获取角色信息Debug.Log("获取角色信息");GetPlayerIdList(socketModel.GetMessage<List<int>>());break;case 5://获取角色数据Debug.Log("获取角色数据");_userDtos = socketModel.GetMessage<List<UserDTO>>();for(int i=0;i<_userDtos.Count;i++){UserDTO userD = _userDtos[i];Debug.Log("name="+ userD.Name);Debug.Log("id=" + userD.Id);Debug.Log("job=" + userD.JobId);}//UIMgr.Instance.Destory(PanelType.SelectPlayerPanel);// UIMgr.Instance.ShowPanel(PanelType.SelectPlayerPanel, _userDtos);break;case 13:Global.Instance.UserDto = socketModel.GetMessage<UserDTO>();if (OnPlayerInfoChange != null)OnPlayerInfoChange();break;...

运行结果:

新建角色成功.再来试试聊天吧.

成功收到信息

 _userHandler.OnPlayerCreate += userCreated;private void userCreated(){Debug.Log("角色新建成功");talk();}

初探基于GameProtocol和NetFrame的RPG服务器相关推荐

  1. 基于VMware vSphere 5.0的服务器虚拟化实践(9)

    基于VMware vSphere 5.0的服务器虚拟化实践(9) 2012-12-31 13:07:57 标签:虚拟化 VMware vSphere 十全十美 原创作品,允许转载,转载时请务必以超链接 ...

  2. 基于第三方开源库的OPC服务器开发指南(2)——LightOPC的编译及部署

    基于第三方开源库的OPC服务器开发指南(2)--LightOPC的编译及部署 前文已经说过,OPC基于微软的DCOM技术,所以开发OPC服务器我们要做的事情就是开发一个基于DCOM的EXE文件.一个代 ...

  3. 基于epoll实现简单的web服务器

    1. 简介 epoll 是 Linux 平台下特有的一种 I/O 复用模型实现,于 2002 年在 Linux kernel 2.5.44 中被引入.在 epoll 之前,Unix/Linux 平台下 ...

  4. Boost:基于boost::asio的延迟udp服务器测试程序

    Boost:基于boost::asio的延迟udp服务器测试程序 实现功能 C++实现代码 客户端源码 服务端源码 实现功能 boost::asio模块,基于boost::asio的延迟udp服务器测 ...

  5. Boost:基于boost::asio的延迟tcp服务器测试程序

    Boost:基于boost::asio的延迟tcp服务器测试程序 实现功能 C++实现代码 客户端源码 服务端源码 实现功能 boost::asio模块,基于boost::asio的延迟tcp服务器测 ...

  6. linux环境下企业基于域名访问的web于电子邮件服务器 论文,基于Linux平台的企业邮件服务器搭建...

    我失骄杨君失柳,杨柳轻飏直上重霄九.得道多助,失道寡助.身后有余忘缩手,眼前无路想回头.鸟宿池边树,僧敲月下门.想当年,金戈铁马,气吞万里如虎. 本文由418133804贡献 pdf文档可能在WAP端 ...

  7. RHEL5 基于虚拟用户验证的VSFTP服务器

    RHEL5基于虚拟用户验证的VSFTP服务器 <?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:off ...

  8. arm Linux 低成本方案,参赛作品《低成本基于ARM+Linux平台搭建web服务器的物联网学习板》...

    [报名阶段需要填写的内容] 1. 参赛者姓名(必填项): 王徕泽 2. 单位或学校名称(选填项): 徕泽电子工作室 3. 当前职务或职称(选填项): 室长 4. 参赛作品的名字(必填项): 低成本基于 ...

  9. 实验:基于keepalived实现两台realserver服务器中的nginx和php-fpm服务互为主从

    基于keepalived实现两台realserver服务器中的nginx和php-fpm服务互为主从 思路:利用两个VIP,一个定位nginx,一个定位php-fpm 步骤: 1.准备两台基于LNMP ...

最新文章

  1. caffe修改需要的东西 6:40
  2. 北航成AAAI 2021最大赢家,两篇一作斩获最佳论文、提名奖,研究皆与Transformer相关...
  3. C++中extern “C”含义深层探索(zz)
  4. Python3 绘制同心圆代码示例
  5. spark mllib和ml类里面的区别
  6. scp和ssh如何连接指定端口的远程主机
  7. linux 省内存的桌面,Linux_在Linux中可视化显示内存占用情况的方法,物理内存不足对Linux桌面系统 - phpStudy...
  8. 【英语学习】【WOTD】darling 释义/词源/示例
  9. DNS原理及其解析过程 精彩剖析
  10. 英文操作系统下WebBrowser控件无法显示本地页面的解决方法
  11. [补档]noip2019集训测试赛(十五)
  12. hdu1520 (树形dp)
  13. 数组中只出现一次的数字(python解法)
  14. USB设备驱动理解(wds)
  15. SM4算法java代码实现
  16. 元宇宙地产演化史:从文本时代到区块链时代
  17. 我们为什么要结婚?(特别有道理)
  18. linux 第十七天 linuxprobe
  19. BZOJ 4408: [Fjoi 2016]神秘数(可持久化线段树)
  20. VS2019 C# MySQL 学生信息增删改查(二、改查(续前节))

热门文章

  1. 自然语言处理项目之文档主题分类
  2. outlook邮箱备份方法:
  3. 开售破发、二手市场跳水 iPhone SE3 “割韭菜”功力大减
  4. 苹果大幅削减iPad产量 将芯片等部件调配给iPhone 13
  5. 阿里影业授出1672.5万份购股权
  6. 美团回应无法使用微信支付:耽误大家干饭了,对不起
  7. 5年iPhone用户换小米11 Ultra:惊叹小米变化大
  8. 水涨船高!造车新势力平均月薪15367元,自动驾驶算法岗年薪可达百万
  9. iPhone 12 mini被质疑锁屏触摸不灵
  10. iPhone销售额第四财季同比下滑21% 苹果市值蒸发约千亿美元