• 您的位置:首页 > 新闻动态 > Unity3D

    unity3d的网络套接字SOCKET模块使用

    2019/6/12      点击:

    使用方法简单:
    1、新建一个空物体把NetWork挂载上,2、填上ip和prot,3、调用Connet方法;

    /*
    UNITY3D 网络组件,它是单例模式。
     通过Network开启和关闭网络;
     消息订阅器MsgEvent管理服务端发来的消息
     */
    using UnityEngine;
    using Assets.Scripts.Events;
    using Assets.Scripts.Util;
    using Assets.Scripts.Net;
    using System;
    
    public enum NetMsgType
    {
        ////// 新消息
        ///newMsg = -4,
        ////// 连接服务中断
        ///interrupt = -3,
        ////// 请求错误
        ///error = -2,
        ////// 连接成功
        ///complete = -1,
        ////// 服务端握手响应
        ///state = 1
    }
    public class Network : MonoBehaviour
    {
        ////// 网络消息广播,当服务端有消息发来是通过它广播
        ///public static readonly EventDispatchMsgEvent = new EventDispatch();
        public static bool IsConnet { get; private set; }
        public static bool IsActive { get; private set; }
        ////// 网络组件实例
        ///public static Network Server { get; private set; }
    
        private SocketTcp socket;
        public string ip = "";
        public int prot;
        void Awake()
        {
            Server = this;
        }
        void Start()
        {
        }
        ////// 向服务端发送数据
        //////协议接口类型///预留///要发送的数据public void outCall(ushort type, ushort error, ByteArray data)
        {
            if (IsConnet)
            {
                socket.Send(type, error, data);
            }
        }
        public void Connet()
        {
            if (IsActive == false)
            {
                IsActive = true;
                socket = new SocketTcp(ip, prot);
                socket.AddListener(NetEvent.CONNETED, Conneted);
                socket.AddListener(NetEvent.CONNET_IOERROT, ConnetIOError);
                socket.AddListener(NetEvent.CONNET_ERROT, ConnetError);
                socket.AddListener(NetEvent.CONNET_MSG, MsgHandler);
    
                socket.Start();
            }
        }
        private void Conneted(EventData data)
        {
            IsConnet = true;
            MsgEvent.Dispatch(NetMsgType.complete);
        }
        void ConnetIOError(EventData data)
        {
            IsConnet = false;
            IsActive = false;
            MsgEvent.Dispatch(NetMsgType.error);
        }
        void ConnetError(EventData data)
        {
            IsConnet = false;
            IsActive = false;
            MsgEvent.Dispatch(NetMsgType.interrupt);
        }
        void MsgHandler(EventData data)
        {
            BucketItem msg = (BucketItem)data.data;
            msg.ByteArray.Position = 0;
            if (msg.Error > 0)
            {
                Debug.Log("网络错误:" + msg.Error);
                return;
            }
            if (Enum.IsDefined(typeof(NetMsgType), msg.Type))
            {
                
                MsgEvent.Dispatch((NetMsgType)msg.Type, msg);
            }
            else
            {
                Debug.Log("未定义网络消息:" + msg.Type);
            }
        }
        public void Disconnect()
        {
            IsConnet = false;
            IsActive = false;
            socket.RemoveListener(NetEvent.CONNETED, Conneted);
            socket.RemoveListener(NetEvent.CONNET_IOERROT, ConnetIOError);
            socket.RemoveListener(NetEvent.CONNET_ERROT, ConnetError);
            socket.RemoveListener(NetEvent.CONNET_MSG, MsgHandler);
            socket.Destroy();
            socket = null;
        }
        void FixedUpdate()
        {
    
        }
        void Update()
        {
            if (socket != null) socket.Updata();
        }
        //程序退出则关闭连接  
        void OnApplicationQuit()
        {
            if (socket != null) socket.Destroy();
        }
    }
    /*
     socketTcp封装了一个轻量通信协议,以8个字节为头部(整型):无符号4字节长度(包括头部8字节)、消息类型无符号2字节、预留无符号2字节。
      使用异步方法兼容hololens的uwp平台。消息类型自定义,协议的数据包格式在注释有说。 */
    using Assets.Scripts.Events;
    using System;
    using System.Collections;
    using Assets.Scripts.Util;
    using UnityEngine;
    using System.Collections.Generic;
    #if !UWP
    using System.Net;
    using System.Net.Sockets;
    #else
    using Windows.Networking.Sockets;
    using Windows.Networking.Connectivity;
    using Windows.Networking;
    #endif
    namespace Assets.Scripts.Net
    {
        public enum NetEvent{
            ////// 连接建立
            ///CONNETED,
            ////// 请求连接服务器时发生错误
            ///CONNET_IOERROT,
            ////// 连接中断
            ///CONNET_ERROT,
            ////// 收到消息
            ///CONNET_MSG
        }
        public class SocketTcp : EventDispatch{
            ////// 头部字节
            ///public const int head_size = 8;
            ////// 是否使用小端
            ///public const bool IsLittleEndian = true;
            bool _activity = false;
            ////// 是否已启用
            ///public bool Activity
            {
                get { return _activity; }
            }
            ////// 接受的消息,队列
            ///private QueueMsgList;
            private QueuecodeMsg;
            public int port { get; private set; }
            public string ip { get; private set; }
            Socket socket;
            SocketAsyncEventArgs ReceiveSaea;
            SocketAsyncEventArgs sendSaea;
            byte[] sendData;
            ////// 允许单个数据包30720字节
            /// 
            ///const int RECV_LEN = 30720;
            byte[] recv_buf = new byte[RECV_LEN];
            Bucket bucket;
            bool socketState=false;
            public SocketTcp(string ip, int port)
            {
                this.port = port;
                this.ip = ip;
                bucket = new Bucket();
                codeMsg = new Queue();
                MsgList = new Queue();
            }
            private SocketAsyncEventArgs connetedSaea;
            ////// 发起链接请求
            /// 会调度CONNETED或CONNET_IOERROT事件
            ///public void Start()
            {
                if (socket==null)
                {
                    try
                    {
                        socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                        connetedSaea = new SocketAsyncEventArgs();
                        connetedSaea.Completed += new EventHandler(connetedCall);//设置回调方法                                                                             
                        connetedSaea.RemoteEndPoint = new IPEndPoint(IPAddress.Parse(ip), port);    //设置远端连接节点,一般用于接收消息
                        sendSaea = new SocketAsyncEventArgs();
                        //连接到服务器
                        socket.ConnectAsync(connetedSaea);
                    }
                    catch (Exception e)
                    {
                        Debug.Log(e);
                        Dispatch(NetEvent.CONNET_IOERROT);
                    }
                    
                }
            }
            private void connetedCall(object sender, SocketAsyncEventArgs e)
            {
                _activity = true;
                codeMsg.Enqueue(NetEvent.CONNETED);
                ReceiveSaea = new SocketAsyncEventArgs();
                ReceiveSaea.SetBuffer(recv_buf, 0, recv_buf.Length);//设置缓冲区
                ReceiveSaea.Completed += new EventHandler(ReceiveCall);//设置回调方法
                //codeMsg.Enqueue(1);
                //连接后开始从服务器读取网络消息
                socket.ReceiveAsync(ReceiveSaea);
            }
            int fragmentLen;
            byte[] fragment;
            ////// 回调读取网络数据、检查是否断线。
            /////////private void ReceiveCall(object sender, SocketAsyncEventArgs e)
            {
                fragmentLen = e.BytesTransferred;//调用这个函数来结束本次接收并返回接收到的数据长度。
                if (fragmentLen > 0)
                {
                    fragment = new byte[fragmentLen];
                    Array.Copy(recv_buf, 0, fragment, 0, fragmentLen);
                    Queuearr = bucket.Infuse(fragment);
                    while (arr.Count > 0)
                    {
                        MsgList.Enqueue(arr.Dequeue());
                    }
                    socket.ReceiveAsync(ReceiveSaea);
                }
                else
                {
                    ReceiveSaea.Dispose();
                    bucket.Reset();
                    socket.Shutdown(SocketShutdown.Receive);//这个函数用来关闭客户端连接 
                    _activity = false;
                    socketState = true;
                    Debug.Log("中断,关闭连接");
                    return;
                }
            }
            ////// 发送字节流
            //////public void Send(ushort type, ushort error, ByteArray data)
            {
                uint len = (uint)data.Length + head_size;
                //清空发送缓存  
                sendData = new byte[len];
                ByteHelp.WriteNumber(len, ref sendData, 0, IsLittleEndian);
                ByteHelp.WriteNumber(type, ref sendData, 4, IsLittleEndian);
                ByteHelp.WriteNumber(error, ref sendData, 6, IsLittleEndian);
                if (data.Length > 0)
                {
                    Array.Copy(data.Data, 0, sendData, head_size, data.Length);
                }
                //sendData.
                //数据类型转换  
                //sendData = Encoding.ASCII.GetBytes(sendStr);
                //发送  
                sendSaea.SetBuffer(sendData, 0, sendData.Length);
                socket.SendAsync(sendSaea);
            }
            ////// 主线程每帧调用以拿取数据
            ///public void Updata()
            {
                while (codeMsg.Count > 0)
                {
                    Dispatch(codeMsg.Dequeue());
                }
                while (MsgList.Count > 0)
                {
                    Dispatch(NetEvent.CONNET_MSG, MsgList.Dequeue());
                }
                if (socketState)
                {
                    //Debug.Log("连接丢失,是否要重连");
                    socketState = false;
                    Dispatch(NetEvent.CONNET_ERROT);
                }
            }
            public void Destroy()
            {
                base.Dispose();
                //后关闭服务器  
                if (socket != null && socket.Connected)
                {
                    //this.socket.Disconnect(true);
                    //this.socket.Shutdown(SocketShutdown.Both);
                    //socket.Dispose();
                    socket.Shutdown(SocketShutdown.Receive);
                    socket.Shutdown(SocketShutdown.Send);
                    ReceiveSaea.Dispose();
                }
                bucket.Reset();
                MsgList.Clear();
                codeMsg.Clear();
                socketState = false;
                if (sendSaea != null) connetedSaea = null;
                if(sendSaea!=null) sendSaea.Dispose();
                sendSaea = null;
                if (_activity)
                {
                    _activity = false;
                }
            }
        }
    }