网络模块
一.客户端
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, //服务器列表 }
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