原创
说实话,ProtocolBuffer这个东西以前一直在用,很多公司在用。找了网上很多教程,在ULUA里怎么用,很少有讲到的。首先,分析一下,既然要热更新,那么协议最好也是能更新的,那肯定不能写C#协议,所以要用LUA能认识的东西,在框架Lua/3rd/插件包里,pbc,pblua,sproto,这3个都可以给LUA来使用的。pbc,是需要将proto文件转为pb文件,然后到LUA里去使用,我以前的公司用的这种办法。pblua,直接可以把proto转成一个Lua脚本,然后给Lua调用。sproto,看了下是直接用LUA写的协议。上面3个具体怎么用看官方例子,很详细。
流程是这样的,一般协议是服务端写好了proto文件,然后我们拿到手把它转成pb文件,拿到LUA里去用,使用ProtocolBuffer的好处就体现出来,服务器那么可能是JAVA的,C++的,我们不用管,服务器那边也有方案处理proto协议,客户端处理好proto协议就行了,两边只需要写一份通用协议就行了,而且数据序列化,解析数据,ProtocolBuffer也做好了,而且比Json需要的流量更小,因为是二进制的,但是由于不方便人阅读,网站还是用JSON的比较多,客户端用ProtocolBuffer,官网的probocol buffers并不支持lua,所以要用上面3个方法之一,来实现protobuf对lua的支持,说实话我现在还没搞懂pb文件到底是个什么东西,为什么必须做成pb文件,以后研究到了再更新,因为pbc不需要生成lua协议描述文件(比如login_pb),一个pb二进制文件就行了,比较简洁效率据说比pbLua高,所以用pbc。
框架官方网络通信的介绍:http://doc.ulua.org/article/ngui/simpleframework_base4.html
ProtocolBuffer相关博客教程:
https://my.oschina.net/sniperLi/blog/530013
http://blog.csdn.net/u014308482/article/details/52958148
http://blog.csdn.net/linshuhe1/article/details/51781749
一. 制作Pb文件
1. ProtocolBuffer 我们不需要源码,直接拿来用,下载地址 :https://github.com/google/protobuf/releases 下载其中的protoc-3.1.0-win32.zip
2.解压后将 protoc.exe 复制到 C:\Windows\System32
3.protoc用法,打开CMD---输入
protoc -h 可以查看protoc的帮助
proto 文件编译到Java文件: protoc -I=D:\ --java_out=D:\ D:\person.proto ( protoc -I=源地址 --输出格式_out=目标地址 源地址/xxx.proto )
proto 文件编译到pb文件: protoc -I=D:\ --descriptor_set_out=D:\addressbook.pb D:\addressbook.proto
4.这里直接用Proto文件生成pd文件,需要自己先创建个文本,改成pb后缀,然后再执行上面的命令,这里有可能我的做法不对,不可能这么弱智,非得自己先建一个,但是我找了半天不知道用什么方法。
二.网络连接
网络这一块发现有2篇不错的博客:http://blog.csdn.net/lyh916/article/details/51096463 http://blog.csdn.net/lyh916/article/details/51250924
讲的是LuaFramework,稍微有点不一样,但原理是一样的,讲的很好。
我写一下我自己的理解:网络连接这块代码,C#代码主要是在Scripts/Network里面,外加一个NetworkManager.cs,Define修改ProtocalType协议类型,服务器消息处理 Network.lua ,消息类型protocal.lua。
连接到到服务器后 SocketCommand.cs会调用Network.lua的OnSocket方法,讲服务器的消息发送给Network模块。然后再添加消息监听AddListener和回调函数。(SocketCommand里也可以再添加C#的消息接受)
测试可以用官方提供的一个服务器源码,也是C#的。
1.连接服务器:
在GameManager.lua里有详细的例子。
AppConst.SocketPort = 2016;
AppConst.SocketAddress = "192.168.1.188";
NetManager:SendConnect();
2.给服务器发送消息:
最底层在在SocketClient类里的 WriteMessage方法向NetworkStream写入byte数组。
一个完整的消息的byte[]包= 消息总长度(ushort两字节) + Protocal消息类型(ushort两字节) + ProtocalType协议类型(byte一字节) + 消息内容(WriteBuffer里写入的byte[])
而且写入的顺序不能换,因为这个官方的服务器就是按照这个字节顺序来读取的。当然可以和服务器协商,看怎么设计消息包的结构。
测试服务端的话,就用官方提高的服务端,服务端在哪下载,怎么配置具体看上一篇博客。
需要修改SuperSocket.SocketService.exe.config配置文件,指定IP和端口号。(注意不要和HttpServer的资源下载端口相同)
<server name="GameService" serverTypeName="GameManagerService" ip="192.168.1.188" port="2016" maxConnectionNumber="1000"></server>
【Lua发送】
buffer:WriteShort(Login); --消息类型(写入Short类型)
buffer:WriteByte(ProtocalType.PBC); --协议类型(写入Byte类型)
buffer:WriteBuffer(code); --消息体(LuaStringBuffer类里的Byte数组)
NetManager:SendMessage(buffer); --/发送消息
【C# 发送】
//所属类必须继承View才能得到NetManager管理器,View继承自MonoBehaviour。
void SendMessage()
{
ByteBuffer buffer = new ByteBuffer();
buffer.WriteShort(Protocal.Login);
buffer.WriteByte(0); // 0是 ProtocalType.BINARY ,回头要自己写一个枚举定义下
buffer.WriteString("这是从C#发来的消息");
buffer.WriteInt(200);
NetManager.SendMessage(buffer);
}
可以先随便发送一个给服务器测试一下,我这边先不做Protobuf的发送,打算下一篇再研究,先实现能发送字符串。
3.接受服务器消息:
注意:服务器发过来消息后,会有一个队列接受,所以只能由lua或者C#来处理,不能同时取一个消息来处理。如果实在要同时处理,那要自己去再添加一个队列,用2个队列,一个给Lua,一个给C#。
【Lua接收】
来到SocketCommand.cs类,就能看到只要接受到服务器的消息,就会执行给Network.lua的OnSocket函数。
switch (buffer.Key) {
default: Util.CallMethod("Network", "OnSocket", buffer.Key, buffer.Value); break;
}
Network.lua的OnSocket函数里广播分发服务器消息。
function Network.OnSocket(key, data) --key(消息类型) data(消息体)
Event.Brocast(tostring(key), data);
end
Event.AddListener(Login, this.OnLogin);实现消息订阅,参数1 消息类型 ,参数2 回调函数
Event.RemoveListener(Login); --移除监听(会节省性能)
【C#接收】
先看,NetworkManager类:
void Update() {
if (sEvents.Count > 0) {
while (sEvents.Count > 0) {
KeyValuePair<int, ByteBuffer> _event = sEvents.Dequeue();
//StartUpCommand实现关联 AppFacade.Instance.RegisterCommand(NotiConst.DISPATCH_MESSAGE, typeof(SocketCommand));
facade.SendMessageCommand(NotiConst.DISPATCH_MESSAGE, _event); //发消息给SocketCommand处理
}
}
}
public override void Execute(IMessage message) {
object data = message.Body;
if (data == null) return;
KeyValuePair<int, ByteBuffer> buffer = (KeyValuePair<int, ByteBuffer>)data; //键值对
//处理一个消息,就会从队列中移除该消息,所以不同把一个消息交给C#又给LUA,只能给其中一个。
// C#的消息 在Protocal类中添加 Lua的消息 在Protocal.lua中添加 值切记不要搞混,比如C#消息值是1000的,Lua的就写1000以后
switch (buffer.Key) {
case Protocal.Login: //这里做演示,把Login消息给C#处理,本来是给Lua处理的。
Debug.LogError("收到了Login消息,协议类型是:" + buffer.Value.ReadByte());
Debug.LogError("收到了Login消息,消息体是:" + buffer.Value.ReadString());
break;
case Protocal.GameMessage: //新加的一个消息类型,值是1000
// Debug.LogError("收到了游戏消息,buffer.Value是:" + buffer.Value.ReadString());
break;
default: //如果不是游戏消息,交给Lua处理
Util.CallMethod("Network", "OnSocket", buffer.Key, buffer.Value);
break;
}
}
C#这边,应该写一个 静态委托类,在 SocketCommand类里按照返回不同的消息执行不同的委托,其他地方去订阅这个消息,具体看我的这篇博文 http://blog.csdn.net/u012322710/article/details/52911937