[Unity热更新]tolua# & LuaFramework(六):网络通信

时间:2021-08-29 18:39:21

一.客户端

AppConst:设置要连接的服务器IP地址和端口号,当要测试更新时,默认使用的是WebUrl


SocketClient

成员:

NetworkStream outStream:负责输出

MemoryStream memStream:负责接收,存储所有接收到的信息

BinaryReader reader:对MemoryStream中的信息进行读取

byte[] byteBuffer:暂存接收到的信息

方法:

void OnReceive(byte[] bytes, int length) :将暂存的信息写入到memStream的末端,然后尝试读取信息,先读取两个字节(信息头,表示该条信息的长度),判断长度,如果能读到一条完整的信息,则将该完整信息交给NetworkManager(以KeyValuePair<int, ByteBuffer>的方式,其中key为消息类型,value为协议类型 + 消息内容),由NetworkManager进行处理,此时NetworkManager会逐条从队列中读取信息,然后交给SocketCommand进行处理,SocketCommand会调用Network.lua中的OnSocket进行处理,传入消息类型和数据,最终消息会在lua中分发。因此,我们可以在lua中写逻辑来发送或者接受信息,因为逻辑使用lua来写的,所以灵活性就很高了。

这里的消息类型是什么呢?以自带例子为例:

客户端protocal.lua

Protocal = {
Connect= '101';--连接服务器
Exception = '102';--异常掉线
Disconnect = '103';--正常断线
Message= '104';--接收消息
}

服务器端Protocal.cs

        public enum Protocal {
//Buildin Table
Connect = 101,//连接服务器
Exception = 102,//异常掉线
Disconnect = 103,//正常断线
Login = 104,//登录游戏
Quit = 105,//离开游戏
ServerTime = 106,//服务器时间
HeartBeat = 107,//心跳数据

//Socket Request Table
Chat = 1001, //User Chat
Friend = 1002, //Friend
Mail = 1003, //Mail
PK = 1004, //PK
Join = 1005, //ComeIn
PayBack = 1006, //支付消息

//Webclient Request Table
Register = 2001, //注册请求
UserInfo = 2002, //用户信息
Buy = 2003, //购买
Sell = 2004, //卖掉
RoleList = 2005, //角色列表
ServerList = 2006, //服务器列表
}


/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

现在梳理一下流程:

1.首先找到Game.lua中的OnInitOK,通过networkMgr:SendConnect();进行连接,然后追踪到SocketClient.cs中的ConnectServer,当连接成功时会回调OnConnect,通过NetworkManager.AddEvent(Protocal.Connect, new ByteBuffer());,把Protocal.Connect作为key放入消息队列中,由Network.lua中的OnSocket进行处理。

而由于Network.lua中的Start方法,通过Event.AddListener(Protocal.Connect, this.OnConnect);得知,最后会输出"Game Server connected!!"

[Unity热更新]tolua# & LuaFramework(六):网络通信


2.先成功运行一下框架自带例子,如无意外,右下方会出现这个,点击这个按钮后会发生什么呢?

[Unity热更新]tolua# & LuaFramework(六):网络通信

找到PromptCtrl.lua中的OnClick,它会对TestProtoType进行判断(其实就是看选择的是什么协议)。找到define.lua,发现框架默认的是TestProtoType = ProtocalType.BINARY;那么好了,回到PromptCtrl.lua,就可以知道当点击按钮后,TestSendBinary就会执行,发送二进制。发送消息类型Protocal.Message,即104,然后服务器端收到了这个104后,通过反射转化为104对应的Login类,消息内容就由Login类进行处理。

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


public void SendMessage(ByteBuffer buffer):对外接口,先写入信息长度,再写入信息。


NetworkManager

对SocketClient的进一步封装,持有一个网络信息队列。同时与Network.lua进行交互。


define.lua:可以设置当前使用的协议类型。这里的协议类型,指的是BINARY、PB_LUA、PBC、SPROTO这四种。

protocal.lua:可以设置传递的消息类型。

Network.lua:管理lua中的网络部分,其中Network.OnMessage(buffer)是对接受信息的处理。在lua中,可以使用networkMgr:SendMessage(buffer);发送信息,具体用法可以看PromptCtrl.lua,它是先写入消息类型,然后是协议类型,最后就是消息内容了。

即:消息 = 消息总长度(两字节) + 消息类型(两字节) + 协议类型(一字节) + 消息内容

Game.lua:找到function Game.OnInitOK(),这里就可以看到要连接的ip地址以及端口号了。真正进行网络连接的是networkMgr:SendConnect();这句话,而不是在c#中进行连接的。



二.服务器端

GameServer:入口文件

Protocal:定义消息类型和协议类型

SocketUtil:

方法:

public static void SendMessage(ClientSession session, Protocal protocal, ByteBuffer buffer):发送消息时,先写入消息的总长度(ushort,两字节),然后是消息的类型(ushort,两字节),最后是消息体( 协议类型 + 消息内容)。

public void OnRequestReceived(ClientSession session, BinaryRequestInfo requestInfo):先ReadShort得到消息的类型,然后将该类型转换为一个类(具体看Message文件夹),然后将这条消息传递给那个类,例子里面是Login这个类,然后会根据协议类型和消息内容反馈给客户端。这里你会看到,从客户端传过来的消息还包含这条消息的长度,但并没有看到其处理,这是因为SuperSocket已经帮我们处理好了,你可以找到ClientReceiveFilter这个类,它继承自FixedHeaderReceiveFilter,即固定消息头,并且使用了base(2)进行初始化,即是指每条从客户端传递过来的消息,SuperSocket都会把前两个字节读取出来,得到消息的长度,然后把剩余的完整的内容反馈给我们(通过requestInfo.Body),显然地SuperSocket肯定帮我们处理好了粘包半包的问题了。


运行结果:

[Unity热更新]tolua# & LuaFramework(六):网络通信

[Unity热更新]tolua# & LuaFramework(六):网络通信



三.结合pblua

修改一下define.lua:TestProtoType = ProtocalType.PB_LUA;

.proto:

message LoginRequest {
required int32 id = 1;
required string name = 2;
optional string email = 3;
}

message LoginResponse {
required int32 id = 1;
}
至于对应的.lua,由框架帮我们生成,在使用时require就可以

发送:

    local login = login_pb.LoginRequest();
login.id = 2000;
login.name = 'game';
login.email = 'jarjin@163.com';
local msg = login:SerializeToString();
----------------------------------------------------------------
local buffer = ByteBuffer.New();
buffer:WriteShort(Protocal.Message);
buffer:WriteByte(ProtocalType.PB_LUA);
buffer:WriteBuffer(msg);
networkMgr:SendMessage(buffer);

接收:

local protocal = buffer:ReadByte();
local data = buffer:ReadBuffer();

local msg = login_pb.LoginResponse();
msg:ParseFromString(data);

LuaByteBuffer:将如pblua、pbc等插件序列化后的数据进行存储


上面所说的是客户端的,服务器端也有.proto以及对应的.cs,也是差不多的操作

四种协议:http://doc.ulua.org/article/ngui/simpleframework_base4.html