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

通常的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

展开阅读全文

没有更多推荐了,返回首页