一.客户端

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(六):网络通信-LMLPHP


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

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

找到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(六):网络通信-LMLPHP

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



三.结合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);

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


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

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


03-16 10:11