您的位置:首页 > 移动开发 > Unity3D

Unity3d-c# Socket异步通讯与Unity组件数据更新的处理

2016-10-24 21:28 465 查看
首先基于C#的Socket的BeginReceive异步接收和BeginSend异步发送数据的底层的实现也是多线程处理,当然也可以自己用线程来实现;C#的异步Socket简单的例子和教程网上很多,在此就不再累赘了;结合刚开始说的,C#的异步Socket实际是多线程实现,那么我们在Unity中使用的时候就会遇到我们不能在C#的Socket的异步回调函数中访问Unity的组件的问题;一开始我试了用事件来处理,就是Socket接收到消息,触发一个事件,在事件回调函数访问Unity的组件,然而这是不可行,查了一下资料了解到事件的回调是在调用线程执行的,也就是说兜了一个圈还是在子线程执行,还是不能访问Unity的组件;涉及到多线程数据的处理,很多时候都会想到使用生产者-消费者模式来异步处理数据,这里我们就可以把异步Socket的线程当作生产者,Unity主线程作为消费者,首先要做的就是创建一个数据容器来存放异步Socket接收到的数据,然后主线程不断的来取数据,如果有数据,那么就把数据交付给对应的处理逻辑;我想的一个简单的实现就是创建一个专门用来派发接收到的数据的组件,也就是一个MonoBehaviour,然后使用每帧执行、固定时间执行函数(update,lateupdate,fixedupdate)或者使用协程来检测数据容器是否有数据,如果有数据就交付给相应的处理逻辑,想法大概就是这样,如下是主要逻辑的简单实现,见笑了!

首先消息简单定义:

public abstract class Message{
public static T Parse<T>(byte[] data)where T:Message {
\\TODO 解析数据,根据自己的数据定义,可以使用工厂模式来产生不同的Message子类
}
}

接下来是消息容器:消息容器比较简单就是一个Message队列

using MessageBuffer = System.Collections.Generic.Queue<Message>;


接下来异步Socket

public class MessageProducer{
public Socket LocalSocket;
public byte[] ReceiveBuffer = new byte[1*1024];
public List<byte> RecieveBytes = new List<byte>();
public MessageBuffer Messages = new MessageBuffer();
//TODO 其他的属性、Socket的初始化、连接到服务器,发送数据等,在此就不写了</span>
public void BeginReceive(){<span style="font-family: Arial, Helvetica, sans-serif;"> </span>
<span style="font-family: Arial, Helvetica, sans-serif;">try</span>
{
socket.BeginReceive(RecieveBuffer,0,1*1024,0 ,new AsyncCallback(OnReceive),this);
}
catch{
//TODO 接收错误处理
}
}
private void OnReceive(IAsyncResult iar){
MessageProducer producer = iar.AsyncState as MessageProducer;
try{int read = producer.LocalSocket.EndReceive(iar);
if(read > 0){
byte[] retbytes = new byte[read];
Array.ConstrainedCopy(producer.ReceiveBuffer ,0,retbytes,0,read);
<span style="white-space:pre"> </span>producer.RecieveBytes.AddRange(retbytes);
<span style="white-space:pre"> </span>if(producer.LocalSocket.Available <= 0){
<span style="white-space:pre"> </span>//TODO 接受完毕,把数据变成Message,正常情况还需要处理粘包,丢包的问题,
<span style="white-space:pre"> </span>//这里假设接收到的都是一个单独完整的数据</span>
<span style="white-space:pre"> </span> Message msg = Message.Parse<ChildOfMessage>(producer.RecieveBytes.ToArray());
<span style="white-space:pre"> </span>RecieveBytes.Clear();
<span style="white-space:pre"> </span>producer.Messages.Enqueue(msg);
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>producer.LocalSocketBeginReceive(RecieveBuffer,0,1*1024,0 ,new AsyncCallback(OnReceive),pproducer);//继续接收数据
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>else{
<span style="white-space:pre"> </span>//服务器断开
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>catch{
<span style="white-space:pre"> </span>//TODO 接收错误处理</span>
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>}//到此,生产的行为结束</span>
}

接下就是Unity 主线程处理消息的代码:
public class MessageConsumer: MonoBehaviour{
private MessageProducer messageproducer = new MessageProducer();</span>
void Start(){
//TODO 生产者的初始化
messageproducer.BeginReceive();//开始接受数据
}
//TODO 这里简单的使用Update函数来检查数据
void Update(){
while(messageproducer.Messages.Count > 0){
Message message = producer.Messages.Dequeue();
//TODO 处理消息,具体的就根据需求来实现了,在此不再多写了
}
}

到这里,主要的代码逻辑就算是完成了;当然这还不算完,这里会有一个问题就是如果在手机上手机进入睡眠状态、锁屏、程序切换等会造成Update方法不执行,但是Socket还是会不停的接收数据,时间长了就会积累很多数据,当切换回游戏是就会需要处理很多消息,有可能很多重复的数据刷新处理,会不会造成卡顿等还没有实际验证过;

到此为止,此文纯属抛砖引玉,我相信大神们肯定有更好的方法,望赐教!拜谢!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐