Socket.IO for Unity 简要介绍和简单应用

原创 2015年11月19日 11:31:15

在项目中使用到了Socket.IO for unity这个Asset Store上免费的库,这里将简要的介绍一下它的结构,已经使用中的注意事项。

目录结构

这里写图片描述
上面为包的目录结构,简单的介绍一下具体的内容:

  • JSONObject - 打包与解析JSON格式
  • Prefabs - 简单的SocketIO客户端的Prefab,实际上其就是一个attach了SocketIOComponent的Unity GameObject
  • Scences - Unity3d的测试Scene,用于简单测试
  • Scripts - Unity3d的MonoBehavior 脚本和其使用到的类, 其实只有一个SocketIOComponent是脚本,其他都是该脚本使用到的帮助类
  • Server - 目录下的存放的是NodeJs服务器端的测试Js脚本,对于于客户端的测试用例,在实际的开发中可以删除。
  • WebSocketSharp- 目录下存放的是C#的WebSocket的实现,不依赖于任何Unity3d的代码。
  • readme.txt - 简单的帮助文档,说明如何使用该package。

核心类解析

  • SocketIOComponent
    SocketIOComponent这个脚本是我们使用该Socket.IO for Unity package最重要的一个类,其集成了报文的封装,解析,回掉函数,Ping,Pong控制帧,以及WebSocket的数据传输。 基本的结构图如下:
    这里写图片描述

如下说明

  1. SocketIOComponent至少使用了两个独立的线程用于WebSocket数据以及控制的传输,至于为什么使用PingThread (Ping和Pong),这个是WebSocket协议控制的需求,详情请https://tools.ietf.org/html/rfc6455#section-5.5
  2. Connect函数用于连接WebScoket 的服务器, Emit函数用于发送数据到服务器,On函数用于注册回掉函数用于处理来自服务器的报文。
  3. Unity3d的Update线程用于服务器发送而来的报文发送,包含ACK报文(客户端传送给服务器的确认回掉)和Event报文(服务器传送给客户端的报文),对于ACK报文,通常是客户端需要同步的获取服务器的消息的时候使用,在该情况下容易产生死锁问题。 解决的办法是新增加一个单独的线程来处理ACK报文和Event消息。(后面会详细介绍如何实现)
  4. SocketIOEvent和JSONObject用于SocketIOComponent的用户的报文的发送与接收。SocketIOEvent如下
        // 事件的名称
        public string name { get; set; }
        // 具体的数据内容
        public JSONObject data { get; set; }
        public SocketIOEvent(string name) : this(name, null) { }
        public SocketIOEvent(string name, JSONObject data)
        {
            this.name = name;
            this.data = data;
        }

        public override string ToString()
        {
            return string.Format("[SocketIOEvent: name={0}, data= {1}]", name, data);
        }
    }
}

如何封装同步函数

SocketIOComponent提供了很简介的方法给我们向服务器发送消息(SocketIOComponent::Emit函数)和服务器接收消息(SocketIOComponent::On函数)。但是其无法让我们从服务器同步的获取消息。 比如说,我们需要从服务器同步的获取数据,当前的实现是无法做到的。修改方法如下:

  1. 客户端
    将SocketIOComponent的Update函数的处理移动到一个新增加的线程中,不依赖于Unity3d的线程。如下:
    public void Connect()
    {
    connected = true;

        socketThread = new Thread(RunSocketThread);
        socketThread.Start(ws);
    
        pingThread = new Thread(RunPingThread);
        pingThread.Start(ws);
    
         *// 新增加callback线程
        callbackThread = new Thread(RunCallbackThread);
        callbackThread.Start(ws);*
    }
    

    private void RunCallbackThread(object obj)
    {
    while(connected)
    {
    // check the msg queue count with the time.
    msgSemaphore.WaitOne(600);
    if (eventQueue.Count > 0 || ackQueue.Count > 0 || wsConnected != ws.IsConnected)
    {
    lock (eventQueueLock)
    {
    while (eventQueue.Count > 0)
    {
    EmitEvent(eventQueue.Dequeue());
    }
    }

                lock (ackQueueLock)
                {
                    while (ackQueue.Count > 0)
                    {
                        InvokeAck(ackQueue.Dequeue());
                    }
                }
    
                if (wsConnected != ws.IsConnected)
                {
                    wsConnected = ws.IsConnected;
                    if (wsConnected)
                    {
                        EmitEvent("connect");
                    }
                    else
                    {
                        EmitEvent("disconnect");
                    }
                }
            }
    
            // GC expired acks
            if(ackList.Count == 0) { continue; }
            if(DateTime.Now.Subtract(ackList[0].time).TotalSeconds < ackExpirationTime) { continue; }
            ackList.RemoveAt(0);  
        }
    

    // 同步的从服务器端获取数据
    private void GetDataFromServerBySync()
    {
    webSocketWaitEvent.Reset();
    SocketIoComponent.Emit(‘testSync’, (JSONObject jsonData) => {
    // The lib always use the to wrapper the json data.
    hallManagerData = HallJson.ToHallManagerData(jsonData [0]);
    webSocketWaitEvent.Set();
    });
    webSocketWaitEvent.WaitOne(500);
    return;
    }

  2. 服务器端
    io.sockets.on(‘connection’, function(socket){
    console.log(‘connection is called!’);
    console.log(socket.id)

    socket.on( ‘testSync’, function(callback){
    // 回调函数
    callback(testJson);
    });
    });

3 做此修改后请注意,回调函数将不在Unity3d的主线程中处理,所以还需要做一点的封装,将最后的事件放入Update线程中处理。 如果没有同步从Server端获取数据的需求,就不要做上述的修改了。

经过初步测试,从服务器端获取超过10k的数据,大约需要4ms左右。服务器为NodeJs,在本地通过127.0.0.1的端口传送。

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

Android设计模式学习之观察者模式

观察者模式在实际项目中使用的也是非常频繁的,它最常用的地方是GUI系统、订阅——发布系统等。因为这个模式的一个重要作用就是解耦,使得它们之间的依赖性更小,甚至做到毫无依赖。以GUI系统来说,应用的UI...

关注CSDN程序人生公众号,轻松获得下载积分

关注公众号 在公众号里回复“”秘密“”两个字 返回 http://task.csdn.net/m/task/home?task_id=398 领取奖励 提示:根据公众号里的自动回复,完成...

属性动画----把图片渐渐变小不见(主函数MainActivity 页面)(XML布局)(本布局和渐变布局一样)

LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schema...

JavaEE 6及以上版本的web.xml问题?

JavaEE 6及以上版本的web.xml问题?MyEclipse JavaEE 6版本开始web.xml突然消失不见?没这回事,只是不太必须而已,有需要的项目可以自行进行添加或在创建项目的时候点击n...

Android 图片毛玻璃的实现方法

注:本文的高斯模糊只能显示,如果想要保存模糊后的图片,请参考另一篇文章:http://blog.csdn.net/fan7983377/article/details/51568059 效果...

目标检测和跟踪小结

一、目标检测目标检测即为从序列图像中将变化区域从背景图像中提取出来。运动目标检测的算法依照目标与摄像机之间的关系可以分为静态背景下运动检测和动态背景下运动检测。1.静态背景 背景差分法 帧间差分法 光...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)