仿LOL项目开发第八天

                                                                       by 草帽

这节我们继续上节所讲的内容,上节我们初始化好了LoginWindow,当我们点击确认选择服务器按钮的时候,就发送服务器id给游戏服务器。

这里就开始涉及到客户端需要跟服务器的网络通信,所以我们得先来写个客户端的Socket框架:

NetworkManager.cs单例脚本来管理。

因为我之前做过了这个脚本,所以我特意封装了自己的网路管理network.dll,直接就可以拿来用。

至于这里面的脚本,有兴趣的童鞋可以自己去反编译下看看。

network.dll下载地址

这个dll下载完成之后,我们新建一个文件夹,取名为Plugin,然后把这个dll拖到里面去。

接着,开始正式的编写NetworkManager脚本:

using UnityEngine;
using System.Collections;
using System;
using Game;
public class NetworkManager : Singleton<NetworkManager>
{
private WWW m_wwwRequest = null;
private float m_fLastTimeForHeartBeat = 0f;
public static string serverURL = "http://127.0.0.1/LOLGameDemo/serverUrl.xml";
public static string serverIP = "127.0.0.1";
public static int serverPort = 8000;
public CNetProcessor m_netProcessor = null;
public CNetwork m_network = null;
private IXLog m_log = XLog.GetLog<NetworkManager>();
public SocketState NetState
{
get
{
SocketState result;
if (null != this.m_network)
{
SocketState state = this.m_network.GetState();
result = state;
}
else
{
result = SocketState.State_Closed;
}
return result;
}
}
public void Init()
{
CRegister.RegistProtocol();
CNetObserver cNetObserver = new CNetObserver();
this.m_netProcessor = new CNetProcessor();
this.m_netProcessor.Observer = cNetObserver;
cNetObserver.oProc = this.m_netProcessor;
this.m_network = new CNetwork();
CPacketBreaker oBreaker = new CPacketBreaker();
string newFullPath = SystemConfig.ResourceFolder;
if (!this.m_network.Init(this.m_netProcessor, oBreaker, 65536u, 65536u, newFullPath, true))
{
this.m_log.Fatal("oNet.Init Error");
}
else
{
this.m_log.Debug("oNet.Init Success!");
this.m_netProcessor.Network = this.m_network;
}
}
public void FixedUpdate()
{
try
{
if (this.m_netProcessor != null && null != this.m_netProcessor.Network)
{
this.m_netProcessor.Network.ProcessMsg();
//this.m_netProcessor.Network.CheckHeartBeat(CommonDefine.IsMobilePlatform);
}
}
catch (Exception ex)
{
this.m_log.Fatal(ex.ToString());
}
}
private IEnumerator ConnectToURL()
{
Debug.Log("ConnectToURL:" + NetworkManager.serverURL);
this.m_wwwRequest = new WWW(NetworkManager.serverURL);
bool flag = false;
for (int i = 0; i < 10; i++)
{
yield return new WaitForSeconds(0.5f);
if (this.m_wwwRequest.isDone)
{
if (string.IsNullOrEmpty(this.m_wwwRequest.error))
{
if (!string.IsNullOrEmpty(this.m_wwwRequest.text))
{
string[] array = this.m_wwwRequest.text.Split(new char[]
{
':'
});
if (array.Length == 2)
{
NetworkManager.serverIP = array[0];//游戏网关服务器ip地址
NetworkManager.serverPort = Convert.ToInt32(array[1]);
flag = true;
this.RealConnectToServer();
}
else
{
Debug.LogError("m_wwwRequest.text=:" + this.m_wwwRequest.text);
}
}
}
else
{
Debug.LogError(this.m_wwwRequest.error.ToString());
}
break;
}
}
if (null != this.m_wwwRequest)
{
this.m_wwwRequest.Dispose();
this.m_wwwRequest = null;
}
if (!flag)
{
/*if (Singleton<Login>.singleton.IsLogined)
{
Singleton<ReConnect>.singleton.OnConnectToURLFailed();
}
else
{
Singleton<Login>.singleton.OnConnectToURLFailed();
}*/
}
yield break;
}
private void RealConnectToServer()
{
this.m_log.Info(string.Format("ConnectToServer:{0}:{1}", NetworkManager.serverIP, NetworkManager.serverPort));
if (!this.m_netProcessor.Network.Connect(NetworkManager.serverIP, NetworkManager.serverPort))
{
Debug.Log("Failed");
/*if (!Singleton<Login>.singleton.IsLogined)
{
Singleton<Login>.singleton.OnConnectFailed();
}
else
{
Singleton<ReConnect>.singleton.OnConnectFailed();
}*/
}
else
{
this.m_log.Info("Connecting...");
}
}
public void ConnectToServer()
{
if (string.IsNullOrEmpty(NetworkManager.serverURL))
{
this.m_log.Error("serverURL IsNullOrEmpty");
}
LOLGameDriver.Instance.StartCoroutine(this.ConnectToURL());
}
public void SendMsg(CProtocol ptc)
{
try
{
if (null != this.m_netProcessor)
{
this.m_netProcessor.Send(ptc);
}
}
catch (Exception ex)
{
this.m_log.Fatal(ex.ToString());
}
}
public void Close()
{
Debug.Log("NetWorkManager::Close");
if (null != this.m_network)
{
this.m_network.Close();
}
if (null != this.m_wwwRequest)
{
this.m_wwwRequest.Dispose();
this.m_wwwRequest = null;
}
}
public void UnInit()
{
Debug.Log("NetWorkManager::OnApplicationQuit");
if (null != this.m_network)
{
this.m_network.UnInit();
this.m_network = null;
}
this.m_netProcessor = null;
}
/// <summary>
/// 发送登陆请求
/// </summary>
/// <param name="username"></param>
/// <param name="password"></param>
/// <param name="serverId"></param>
public void SendLogin(string username, string password, int serverId)
{
CptcC2GReq_Login instance = new CptcC2GReq_Login()
{
m_strUsername = username,
m_strPassword = password,
m_dwServerId = serverId
};
SendMsg(instance);
}
}

因为客户端和服务器是有协议约定的,所以我们每条消息都必须准守这个协议,这里我在network.dll里面就搞了个协议抽象基类Cptrocol.cs,然后自己在Unity中新建CRegister.cs来管理这些消息。

CRegister:

using UnityEngine;
using System.Collections;
namespace Game
{
internal class CRegister
{
/// <summary>
/// 注册消息协议
/// </summary>
public static void RegistProtocol()
{
CProtocol.Register(new CptcG2CNtf_LoginResult());//1001,注册登录消息
}
}
}

可以有些童鞋还会有报错,这里我们得在写个类:CNetObserver:主要用于接收消息的监控

using UnityEngine;
using System.Collections;
using Utility;
namespace Game
{
public class CNetObserver : INetObserver
{
public CNetProcessor oProc = null;
public void OnConnect(bool bSuccess)
{
if (!bSuccess)
{
}
else
{
XLog.Log.Debug("Connect Success!");
}
}
public void OnClosed(NetErrCode nErrCode)
{
XLog.Log.Error(string.Format("OnClosed:{0}", nErrCode.ToString()));
}
public void OnReceive(int unType, int nLen)
{
Singleton<PerformanceAnalyzer>.singleton.OnRecevie((uint)nLen);
}
public void OnSend(int dwType, int nLen)
{
Singleton<PerformanceAnalyzer>.singleton.OnSend((uint)nLen);
}
}
}

OK,这样大致的网络通信的单例脚本就写完了。这里我讲下大致的连接步骤流程。

首先,我们点击确认选择按钮,先判断是否已经连接上网关服务器,如果没有就开始连接。

那么怎么个连接法,我这里采用的是,先访问web站点,从站点中获取网关服务器ip地址和端口号。这样的好处是能动态的登陆网关服务器,比如网关1崩溃了,我只要改下web站点的ip就可以了,如果在程序内写死的话,这样又得重新更新客户端,麻烦。

大家可以看到我这边写的是我自己的web站点地址,这里,你们得改成你们自己写的。

这里我写的ip地址是127.0.0.1,端口号8000,因为我们就在本地测试,所以就写本地ip,如果是腾讯或者阿里云的话,我们就改成他的ip地址即可。

然后我们回到LoginCtrl里面,添加一个方法LoginStart,传递的参数是选择的服务器id:

    public void LoginStart(int serverId)
{
//如果已经连接上,就直接发送登陆消息请求
if (Singleton<NetworkManager>.singleton.NetState == SocketState.State_Connected)
{
SystemConfig.SelectedServerIndex = serverId;//保存上次选择的服务器id
SystemConfig.LocalSetting.SelectedServer = serverId;//保存到本地配置
//发送登陆消息请求
NetworkManager.singleton.SendLogin(this.username,this.password,serverId);
}
else
{
Singleton<NetworkManager>.singleton.ConnectToServer();//开始连接服务器
}
}

然后把这个方法添加到LoginWindow的SelectServerButton当做事件监听。

    /// <summary>
/// 登陆游戏服务器
/// </summary>
/// <param name="go"></param>
public void OnLoginServer(GameObject go)
{
if (this.m_selectedServerId >= 0)
{
LoginCtrl.singleton.LoginStart(this.m_selectedServerId);
}
else
{
Debug.LogError("选择服务器不存在");
}
}

OK,我们回到NetworkManager中,来写SendLogin这方法,主要是发送登陆消息给游戏网关服务器。

    /// <summary>
/// 发送登陆请求
/// </summary>
/// <param name="username"></param>
/// <param name="password"></param>
/// <param name="serverId"></param>
public void SendLogin(string username, string password, int serverId)
{
CptcC2GReq_Login instance = new CptcC2GReq_Login()
{
m_strUsername = username,
m_strPassword = password,
m_dwServerId = serverId
};
SendMsg(instance);
}

CptcC2GReq_Login这条就是登陆消息,遵守CProtocol

using UnityEngine;
using System.Collections;
using Game;
/// <summary>
/// 客户端发送给服务器的登陆消息请求
/// </summary>
public class CptcC2GReq_Login : CProtocol
{
public string m_strUsername;//用户名
public string m_strPassword;//密码
public int m_dwServerId;//选择的服务器id
public CptcC2GReq_Login()
: base(1000)
{
this.m_strUsername = "";
this.m_strPassword = "";
this.m_dwServerId = 0;
}
public override CByteStream DeSerialize(CByteStream bs)
{
bs.Read(ref m_strUsername);
bs.Read(ref m_strPassword);
bs.Read(ref m_dwServerId);
return bs;
}
public override CByteStream Serialize(CByteStream bs)
{
bs.Write(this.m_strUsername);
bs.Write(this.m_strPassword);
bs.Write(this.m_dwServerId);
return bs;
}
public override void Process()
{ }
}

可见这条消息包括3个需要传递的变量:

    public string m_strUsername;//用户名
public string m_strPassword;//密码
public int m_dwServerId;//选择的服务器i

有用户名、密码、还有选择的游戏服务器id。

OK,客户端的通信已经完成,接着我们就得开始搭建服务器,我这里采用的是java的mina框架。

不懂的童鞋可以去先了解一下,很快就能入门。

CptcC2GReq_Login

最新文章

  1. wamp 局域网访问
  2. VMware下虚拟机的转移
  3. 【转】去掉Sqlite3 数据库中的前后回车换行符(newline)
  4. 为网格布局图片打造的超炫 CSS 加载动画
  5. mysql 如何用一条SQL将一张表里的数据插入到另一张表 3个例子
  6. 设置二级域名共享一级域名Cookie和删除共享Cookie
  7. Unity3D独立游戏开发日记(一):动态生成树木
  8. ecshop的几个小瑕疵
  9. linux下解决端口被占用的问题
  10. POJ 1781
  11. TCP/IP 与OSI结构图
  12. Hits算法
  13. iOS lanchImage 和icon的设置
  14. 记录一次EF优化
  15. 配置exVim开发环境
  16. 自学Aruba之路
  17. java正则表达式取出匹配字符串
  18. 安装LNMP笔记
  19. REST API 调用 方法
  20. “此flash与您的地区不兼容”

热门文章

  1. spark java API 实现二次排序
  2. C#和PHP 长整型时间互转
  3. keras Variable set_value错误解决
  4. Codeforces Round #461 (Div. 2)
  5. 谈应用环境下的TIME_WAIT和CLOSE_WAIT[转]
  6. [USACO5.5]Hidden Password
  7. 一个完成的spring xml配置文件
  8. servlet生命周期和执行流程
  9. Linux服务器性能检测命令集锦
  10. Sql server 存储过程基础语法