一.客户端
AppConst:设置要连接的服务器IP地址和端口号,当要测试更新时,默认使用的是WebUrl
SocketClient
成员:
NetworkStream outStream:负责输出
MemoryStream memStream:负责接收,存储所有接收到的信息
BinaryReader reader:对MemoryStream中的信息进行读取
byte[] byteBuffer:暂存接收到的信息
方法:
void OnReceive(byte[] bytes, int length) :将暂存的信息写入到memStream的末端,然后尝试读取信息,先读取两个字节(信息头,表示该条信息的长度),判断长度,如果能读到一条完整的信息,则将该完整信息交给NetworkManager(以KeyValuePair
这里的消息类型是什么呢?以自带例子为例:
客户端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!!"
2.先成功运行一下框架自带例子,如无意外,右下方会出现这个,点击这个按钮后会发生什么呢?
找到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肯定帮我们处理好了粘包半包的问题了。
运行结果:
三.结合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 = '[email protected]';
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);
上面所说的是客户端的,服务器端也有.proto以及对应的.cs,也是差不多的操作
四种协议:http://doc.ulua.org/article/ngui/simpleframework_base4.html