您的位置:首页 > 新闻动态 > 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;
            }
        }
    }
}