【Unity&C#】运行在主线程的Socket

来源:互联网 发布:react.js阮一峰 编辑:程序博客网 时间:2024/06/05 01:07

通常的Socket都是通过多线程的方式来实现的,多线程需要确保线程安全,而且代码量也会相对多一些,由于之前已经实现了Unity的协程功能,现在就可以通过协程来实现单线程的Socket了。
首先,封装一下C#的Socket。

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Net;using System.Net.Sockets;using FirBase;namespace NxNetwork{    public class NxSocket    {        private Socket _socket;        public NxSocket()        {            _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);        }        public bool Connect(string ip, int port)        {            return Connect(new IPEndPoint(IPAddress.Parse(ip), port));        }        public bool Connect(IPEndPoint ipPoint)        {            bool connected = false;            try            {                FirLog.v(this, "连接服务器:" + ipPoint.Address.ToString());                _socket.NoDelay = true;                _socket.Connect(ipPoint);                _socket.Blocking = false;                connected = true;            }            catch(Exception ex)            {                FirLog.v(this, "连接出现异常:" + ex.Message);            }            return connected;        }        public bool Disconnect()        {            bool success = false;            try            {                _socket.Shutdown(SocketShutdown.Both);                _socket.Close();                success = true;            }            catch (Exception ex)            {                FirLog.v(this, "断开连接出现异常:" + ex.Message);            }            return success;        }        public bool Send(byte[] buf, int lenght)        {            bool success = false;            try            {                _socket.Send(buf, lenght, SocketFlags.None);                success = true;            }            catch(SocketException ex)            {                FirLog.v(this, "发送出现异常:" + ex.Message);            }            return success;        }        public int Receive(byte[] buf, int offset, int size)        {            return _socket.Receive(buf, offset, size, SocketFlags.None);        }        public int Receive(ref byte[] buf)        {            int lenght = 0;            try            {                lenght = _socket.Receive(buf);            }            catch(Exception ex)            {                FirLog.v(this, "接受出现异常:" + ex.Message);            }            return lenght;        }        public int Available        {            get            {                try                {                    if (_socket != null)                    {                        lock (_socket)                        {                            return _socket.Available;                        }                    }                }                catch(Exception ex)                {                    FirLog.v(this, "Available出现异常:" + ex.Message);                }                return 0;            }        }        public bool Connected        {            get            {                if(_socket != null && _socket.Connected)                {                    return true;                }                return false;            }        }        public void Dispose()        {            if(_socket != null)            {                lock(_socket)                {                    if (_socket.Connected)                    {                        _socket.Shutdown(SocketShutdown.Both);                    }                    _socket.Close();                    _socket = null;                }            }            FirLog.v(this, "析构Socket");        }    }}

这只是包装了一些Socket的接口,可以看到,我们包装的这个Socket是通过NoBlock的方式来接受数据的,所以接收数据的方法Receive不会阻塞线程,这就给我们的协程提供了可能。
下面是通过协程实现的NetworkManager

using FirBase;using NxNetwork.MSG;using System;using System.Collections;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace NxNetwork{    public class NetworkNoThreadManager : FirSingleton<NetworkNoThreadManager>    {        private NxSocket _socket = new NxSocket();        private byte[] _recvData = new byte[GlobalVar.BUFFER_LENGTH];       //消息接受缓存        private byte[] _recvDataTmp = new byte[GlobalVar.BUFFER_LENGTH];        private UInt32 _recvOffset = 0;        Dispatcher _commDispatcher = new Dispatcher();        public NetworkNoThreadManager()        {        }        public void RegisterMsg<T>(CallBack<T> handle)        {            _commDispatcher.Reg(handle);        }        public void Init()        {            FirTask.Instance.AddTask(Recv());        }        public void ConnectServer(string ip, int port)        {            _socket.Connect(ip, port);        }        public void SendMsg<T>(T data)        {            var bData = SerializationHelper.Serialization(data);            ProtoMsg msg = new ProtoMsg(bData.Length, DateTime.Now.ToBinary(), data.GetType().Name, bData);            var msgData = msg.ToBinary();            SendMsg(msgData);        }        public void SendMsg(byte[] data)        {            _socket.Send(data, data.Length);            if (_socket == null || !_socket.Connected)            {                //TODO 重连            }        }        private IEnumerator Recv()        {            while (true)            {                if (_socket != null && _socket.Connected)                {                    if (_socket.Available > 0)                    {                        int receiveLength = _socket.Receive(ref _recvData);                        if (receiveLength != 0)                        {                            Array.Copy(_recvData, 0, _recvDataTmp, _recvOffset, receiveLength);                            _recvOffset += (UInt32)receiveLength;                            if (_recvOffset > GlobalVar.HEAD_SIZE)                            {                                byte[] receiveData = new byte[_recvOffset];                                Array.Copy(_recvDataTmp, receiveData, _recvOffset);                                Array.Clear(_recvDataTmp, 0, _recvDataTmp.Length);                                _recvOffset = 0;                                RecvDataHandle(receiveData);                            }                        }                    }                }                yield return new WaitForEndOfFrame();            }        }        private void RecvDataHandle(byte[] receiveData)        {            ProtoMsg msg = SerializationHelper.BinaryDeSerialization<ProtoMsg>(receiveData);            byte[] data = new byte[msg.Lenght];            Array.Copy(msg.MsgBuffer, data, msg.Lenght);            _commDispatcher.MsgDispatcher(msg.MsgName, data);        }    }}

在main函数需要这样启动

NetworkNoThreadManager.Instance.Init();            NetworkNoThreadManager.Instance.ConnectServer("127.0.0.1", 8885);

之后我们可以像上一篇博客那样注册消息,首先建立一个消息处理类:

    class LoginHandle    {        public LoginHandle()        {            NetworkNoThreadManager.Instance.RegisterMsg(new CallBack<UserInfo>(onUserInfo));        }        public void onUserInfo(UserInfo info)        {            FirLog.v(this, "接受到用户信息:" + info.accid + "密码:" + info.pwd);        }    } 

然后同样在main函数中创建这个类。

LoginHandle handle = new LoginHandle();

然后就可以接受消息了

源码以及测试工程
http://download.csdn.net/detail/baijiajie2012/9481989

1 0
原创粉丝点击