unity3d的網絡套接字SOCKET模塊使用
2019/6/12      點擊:
	使用方法簡單:
1、新建一個空物體把NetWork掛載上,2、填上ip和prot,3、調用(yòng)Connet方法(fǎ); 
/*
UNITY3D 網(wǎng)絡組件,它是單例模式。
 通(tōng)過Network開啟和關閉網絡;
 消息訂閱器MsgEvent管理服務端(duān)發來的(de)消息
 */
using UnityEngine;
using Assets.Scripts.Events;
using Assets.Scripts.Util;
using Assets.Scripts.Net;
using System;public enum NetMsgType
{
    ////// 新消息
    ///newMsg = -4,
    ////// 連接服務中斷(duàn)
    ///interrupt = -3,
    ////// 請求錯誤
    ///error = -2,
    ////// 連接(jiē)成功
    ///complete = -1,
    ////// 服務端(duān)握手響應
    ///state = 1
}
public class Network : MonoBehaviour
{
    ////// 網絡消息廣播,當(dāng)服務端(duān)有消息發來是通過它廣播
    ///public static readonly EventDispatchMsgEvent = new EventDispatch();
    public static bool IsConnet { get; private set; }
    public static bool IsActive { get; private set; }
    ////// 網絡組件實例(lì)
    ///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("網絡錯(cuò)誤(wù):" + 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();
    }
    //程序退出則關閉(bì)連接(jiē)  
    void OnApplicationQuit()
    {
        if (socket != null) socket.Destroy();
    }
}
/*
 socketTcp封裝了一個輕(qīng)量通(tōng)信協議,以8個字節為頭部(整型):無符號4字節長(zhǎng)度(包括頭部8字節)、消息類型無符號2字節、預留無符號2字節。
  使用異步方法兼容(róng)hololens的uwp平台。消息類型自定義,協(xié)議的數據包格(gé)式在注釋有說。 */
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,
        ////// 請求連(lián)接服務器時發生錯誤
        ///CONNET_IOERROT,
        ////// 連(lián)接中斷
        ///CONNET_ERROT,
        ////// 收到消(xiāo)息
        ///CONNET_MSG
    }
    public class SocketTcp : EventDispatch{
        ////// 頭部字節
        ///public const int head_size = 8;
        ////// 是否使用小端
        ///public const bool IsLittleEndian = true;
        bool _activity = false;
        ////// 是否已(yǐ)啟用
        ///public bool Activity
        {
            get { return _activity; }
        }
        ////// 接受的消息(xī),隊列
        ///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;
        ////// 發起鏈接請(qǐng)求(qiú)
        /// 會調度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);//設置回調方(fāng)法                                                                             
                    connetedSaea.RemoteEndPoint = new IPEndPoint(ipadsdress.Parse(ip), port);    //設置遠端連接節點,一般用(yòng)於接收消(xiāo)息
                    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);//設置回(huí)調(diào)方法
            //codeMsg.Enqueue(1);
            //連接後開始從服務(wù)器(qì)讀取網絡消息
            socket.ReceiveAsync(ReceiveSaea);
        }
        int fragmentLen;
        byte[] fragment;
        ////// 回調讀取網絡數(shù)據、檢查是否斷線。
        /////////private void ReceiveCall(object sender, SocketAsyncEventArgs e)
        {
            fragmentLen = e.BytesTransferred;//調用這個函數來結束本次接(jiē)收並返回接收到的數據(jù)長(zhǎng)度。
            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);//這個函數用來關閉客戶端連(lián)接 
                _activity = false;
                socketState = true;
                Debug.Log("中(zhōng)斷,關閉連接");
                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();
            //後關閉服(fú)務器  
            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;
            }
        }
    }
}- 上一篇:UNITY3D使用SHADER給頂點設(shè)置顏色 2019/6/12
- 下一篇:Unity3d網絡通信 - NetWork組件使用 2019/5/28



