Skip to main content
 首页 » 资源教程 » Unity3D教程

Unity3D热更新LuaFramework入门实战(6)——网络

2016年09月21日 08:47:5913300蛮牛网

如今大部分的游戏都是网络游戏,网络游戏便涉及到网络连接发起、网络数据接收等内容。LuaFramework内置了网络模块(NetworkManager、SocketClient、ByteBuffer、Converter、Protocal),本篇将会介绍该模块的调用方法以及其原理。由于不是基础章节,文章不会详细讲解基础知识,如果读者想要了解3D网络相关的内容,可以参考笔者即将出版的书籍《Unity3D网络游戏实战》中第六章“网络基础”。

1、发起连接

发起连接是客户端网络通信的第一步,LuaFramewor中,只需通过LuaFramework.AppConst.SocketAddress和LuaFramework.AppConst.SocketPort设置ip和端口,然后调用NetworkManager的SendConnect方法即可发起连接。Main.lua的代码如下:

require "Network"

--主入口函数。从这里开始lua逻辑

function Main()

        local LuaHelper = LuaFramework.LuaHelper

local networkMgr = LuaHelper.GetNetManager()

local AppConst = LuaFramework.AppConst

AppConst.SocketPort = 1234;

AppConst.SocketAddress = "127.0.0.1";

networkMgr:SendConnect();

end

在收到服务端回应后,LuaFramework会调用Network的OnSocket方法(写死)。新建名为Network.lua的文件,处理消息回调。在如下的代码中,Protocal代表协议号,比如“连接服务器”(Protocal.Connect)的协议号是101,在OnSocket的参数中,key便是收到的协议号,data是收到的数据。

Network = {};

--协议

Protocal = {

Connect        = '101';        --连接服务器

Exception   = '102';        --异常掉线

Disconnect  = '103';        --正常断线   

Message        = '104';        --接收消息

}

--Socket消息--

function Network.OnSocket(key, data)

if key == 101 then

LuaFramework.Util.Log('OnSocket Connect');

else

LuaFramework.Util.Log('OnSocket Other');

end

end

为了测试网络功能,需要编写服务端,这里使用c#编写一套简单的服务端程序,仅为调试使用,代码如下:

using System;

using System.Net;

using System.Net.Sockets;

using System.Linq;

class MainClass

{

public static void Main(string[] args)

{

Console.WriteLine("Hello World!");

//Socket

Socket listenfd = new Socket(AddressFamily.InterNetwork,

         SocketType.Stream, ProtocolType.Tcp);

//Bind

IPAddress ipAdr = IPAddress.Parse("127.0.0.1");

IPEndPoint ipEp = new IPEndPoint(ipAdr, 1234);

listenfd.Bind(ipEp);

//Listen

listenfd.Listen(0);

Console.WriteLine("[服务器]启动成功");

while (true)

{

//Accept

Socket connfd = listenfd.Accept();

Console.WriteLine("[服务器]Accept");

}

}

}

运行服务端和客户端,客户端会发起连接,服务端accept该连接后回应,客户端会显示“OnSocket Connect”

Unity3D热更新LuaFramework入门实战(6)——网络 Unity3D教程 第1张

图:服务端

Unity3D热更新LuaFramework入门实战(6)——网络 Unity3D教程 第2张

图:客户端

此时把服务端关掉(断开连接),客户端会收到协议号为102的消息,即异常掉线(Exception)。

Unity3D热更新LuaFramework入门实战(6)——网络 Unity3D教程 第3张

图:异常掉线

调用NetworkManager.SendConnect实际是调用BeginConnect发起连接。连接之后,回调OnConnect方法。

Unity3D热更新LuaFramework入门实战(6)——网络 Unity3D教程 第4张

图:连接过程

OnConnect方法调用NetworkManager.AddEvent,排除设计模式的内容,相当于调用Network.lua的OnSocket方法。传入OnSocket的第1个参数为101(Protocal.Connect),指代协议名,第2个参数是空的字节流。网络模块中定义了101、102、103这3个固定的协议号,分别代表连接服务器、异常断线和正常断线。

Unity3D热更新LuaFramework入门实战(6)——网络 Unity3D教程 第5张

图:连接回调

2、发送和接收

接下来尝试发送和接收数据。LuaFramework默认(如果不去改它的代码)使用的协议格式如下图所示,前面的2个字节为消息长度,用于处理沾包分包,随后的2个字节代表协议号(如上面的101、102、103),最后才是消息的内容。

Unity3D热更新LuaFramework入门实战(6)——网络 Unity3D教程 第6张

图:协议

修改Network.lua,在连接成功后(OnSocket方法的101协议),调用send发送一串协议号为104的数据。服务端收到数据后回射给客户端,客户端在收到回应后(OnSocket方法的104协议),读取并显示出来。

send方法中新建了一个buffer,然后往buffer中添加协议号(104)和协议内容(字符串:《Unity3D网络游戏实战》是一本好书!),最后调用networkMgr:SendMessage()发送数据。networkMgr:SendMessage()会自动计算协议长度,并附加到buffer上发送出去。

--Socket消息--

function Network.OnSocket(key, data)

if key == 101 then

LuaFramework.Util.Log('OnSocket Connect');

Send()

elseif key == 104 then

LuaFramework.Util.Log('OnSocket Message ');

local str = data:ReadString();

LuaFramework.Util.Log('收到的字符串:'..str);

else

LuaFramework.Util.Log('OnSocket Other '..key);

end

end

function Send()

--组装数据

local buffer = LuaFramework.ByteBuffer.New();

buffer:WriteShort(Protocal.Message);

buffer:WriteString("《Unity3D网络游戏实战》是一本好书!");

--发送

local LuaHelper = LuaFramework.LuaHelper

local networkMgr = LuaHelper.GetNetManager()

networkMgr:SendMessage(buffer);

LuaFramework.Util.Log('数据发送完毕');

end

修改服务端程序,读出接收到的内容,并echo回去。

public static void Main(string[] args)

{

略,没有改动

while (true)

{

//Accept

Socket connfd = listenfd.Accept();

Console.WriteLine("[服务器]Accept");

//Recv 不考虑各种意外,只做测试

byte[] readBuff = new byte[100];

int count = connfd.Receive(readBuff);

//显示字节流

string showStr = "";

for (int i = 0; i < count; i++)

{

int b = (int)readBuff;

showStr += b.ToString() + " ";

}

Console.WriteLine("[服务器接收]字节流:"+ showStr);

//解析协议

Int16 messageLen = BitConverter.ToInt16(readBuff,0);

Int16 protocal = BitConverter.ToInt16(readBuff,2);

Int16 strLen = BitConverter.ToInt16(readBuff,4);

string str = System.Text.Encoding.UTF8.GetString(readBuff, 6, strLen);

Console.WriteLine("[服务器接收] 长度:" + messageLen);

Console.WriteLine("[服务器接收] 协议号:" + protocal);

Console.WriteLine("[服务器接收] 字符串:" + str);

//Send(echo)

byte[] writeBuff = new byte[count];

Array.Copy(readBuff,writeBuff,count);

connfd.Send(writeBuff);

}

}

运行游戏,可以看到服务端收到的如图所示的信息。字节流的前两位“53 0”表示消息长度为53字节,紧跟着的“104 0”代表协议号104。在字符串的封装中(buffer:WriteString),程序会先在buffer中添加字符串的长度,最后才是字符串的内容。“49 0”即表示“《Unity3D网络游戏实战》是一本好书!”占用49个字节(14个中文符号,每个3字节,7个英文符号,每个1字节)。协议长度53字节 = 协议号2个字节 + 字符串长度2字节 + 字符串内容49字节。

Unity3D热更新LuaFramework入门实战(6)——网络 Unity3D教程 第7张

图:服务端收到的信息

客户端收到服务端回射的消息后,也会显示出来,如下图所示。

Unity3D热更新LuaFramework入门实战(6)——网络 Unity3D教程 第8张

图:客户端收到的消息

在lua中调用networkMgr:SendMessage(buffer)时,实际上相当于调用了SocketClient的WriteMessage方法,该方法会计算协议的长度,然后将长度和内容组装在一起,调用BeginWrite发送数据。

Unity3D热更新LuaFramework入门实战(6)——网络 Unity3D教程 第9张

图:发送数据

在建立连接后,SocketClient会调用BeginRead,当收到服务端的消息时,回调OnRead方法。OnRead又调用了OnReceive方法。

Unity3D热更新LuaFramework入门实战(6)——网络 Unity3D教程 第10张

图:接收数据过程

OnReceive方法完成沾包分包处理,然后调用AddEvent方法分发消息(相当于调用了lua中NetWork表的OnSocket方法)。

Unity3D热更新LuaFramework入门实战(6)——网络 Unity3D教程 第11张

图:解析数据过程

关于BeginRead、BeginConnect等方法的介绍,读者可以查看c#网络编程的资料或参照《Unity3D网络游戏实战》第6章“网络基础”。

3、消息分发

一款游戏往往涉及很多条网络通信协议,在Network.OnSocket中,如果只用ifelse语句处理不同协议,代码往往会混乱不堪。LuaFramework集成了消息分发的方法,用法如下所示。

1、引用LuaFramework\Lua\events.lua,然后使用Event.AddListener添加监听,例如“Event.AddListener(Protocal.Connect, Network.OnConnect); ”表示当收到101协议(Protocal.Connect)时,回调Network.OnConnect方法。Main.lua代码如下:

require "Network"

Event = require 'events'

--主入口函数。从这里开始lua逻辑

function Main()

local LuaHelper = LuaFramework.LuaHelper

local networkMgr = LuaHelper.GetNetManager()

local AppConst = LuaFramework.AppConst

AppConst.SocketPort = 1234;

AppConst.SocketAddress = "127.0.0.1";

Event.AddListener(Protocal.Connect, Network.OnConnect); 

Event.AddListener(Protocal.Message, Network.OnMessage); 

networkMgr:SendConnect();

end

2、在需要分发消息的地方调用Event.Brocast,然后编写相应的回调函数。Network.lua的部分代码如下:

--Socket消息--

function Network.OnSocket(key, data)

LuaFramework.Util.Log('OnSocket 消息分发:'..key);

Event.Brocast(tostring(key), data);

end

function Network.OnConnect(data) 

LuaFramework.Util.Log('Network.OnConnect');

Send()

end

function Network.OnMessage(data) 

LuaFramework.Util.Log('Network.OnMessage');

local str = data:ReadString();

LuaFramework.Util.Log('收到的字符串:'..str);

end

运行游戏,可以看到消息分发的结果。

Unity3D热更新LuaFramework入门实战(6)——网络 Unity3D教程 第12张

图:消息分发

调用Event.AddListener,实际上是在一个表中添加数据,把某个协议号对应于某个方法的信息记录起来。

Unity3D热更新LuaFramework入门实战(6)——网络 Unity3D教程 第13张

图:AddListener的过程

当调用Event.Brocast时,程序会查找这份表,然后执行回调方法。这里使用了协程来调用回调函数。使用协程的目的应该是不让回调逻辑阻碍主体逻辑,然而由于协程是单线程的,这点不起作用。除非回调函数也使用协程,相互配合。所以这里应该可以不用协程的。

Unity3D热更新LuaFramework入门实战(6)——网络 Unity3D教程 第14张

图:Brocast的过程

至此,读者应该理解LuaFramework网络模块的使用方法了。《Unity3D网络游戏实战》中,第六章“网络基础”,第七章“服务端框架”,第八章“客户端网络模块”都讲解网络有关的内容,并介绍一套网络框架的实现方法,相信对读者能有帮助。


阅读延展
评论列表暂无评论
发表评论