封装unity socket模块,以兼容模式支持Hololens平台。

点击下载类库
点击下载demo(服务端+u3d)
开发需要自己封装了socket模块,共享给小伙伴。
作者邮箱:zh.360@qq.com
使用方法简单:
1、新建一个空物体把NetWork挂载上,2、填上ip和prot,3、调用Connet方法;
这里写图片描述
消息类型要自定义,协议的数据包格式在注释有说。如果有需要的可以评论告诉我,我做一个完整的demo上传。
直接上核心代码

/*
 网络组件,它是单例模式。
 通过Network开启和关闭网络;
 消息订阅器MsgEvent管理服务端发来的消息
 @author jzh 2018.05.14 email:zh.360@qq.com
 */
using UnityEngine;
using Assets.Scripts.Events;
using Assets.Scripts.Util;
using Assets.Scripts.Net;
using System;

public enum NetMsgType
{
    /// <summary>
    /// 新消息
    /// </summary>
    newMsg = -4,
    /// <summary>
    /// 连接服务中断
    /// </summary>
    interrupt = -3,
    /// <summary>
    /// 请求错误
    /// </summary>
    error = -2,
    /// <summary>
    /// 连接成功
    /// </summary>
    complete = -1,
    /// <summary>
    /// 服务端握手响应
    /// </summary>
    state = 1
}
public class Network : MonoBehaviour
{
    /// <summary>
    /// 网络消息广播,当服务端有消息发来是通过它广播
    /// </summary>
    public static readonly EventDispatch<NetMsgType> MsgEvent = new EventDispatch<NetMsgType>();
    public static bool IsConnet { get; private set; }
    public static bool IsActive { get; private set; }
    /// <summary>
    /// 网络组件实例
    /// </summary>
    public static Network Server { get; private set; }

    private SocketTcp socket;
    public string ip = "";
    public int prot;
    void Awake()
    {
        Server = this;
    }
    void Start()
    {
    }
    /// <summary>
    /// 向服务端发送数据
    /// </summary>
    /// <param name="type">协议接口类型</param>
    /// <param name="error">预留</param>
    /// <param name="data">要发送的数据</param>
    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平台。
  处理了粘包
  @author jzh 2018.04.27  email:zh.360@qq.com
 */
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{
        /// <summary>
        /// 连接建立
        /// </summary>
        CONNETED,
        /// <summary>
        /// 请求连接服务器时发生错误
        /// </summary>
        CONNET_IOERROT,
        /// <summary>
        /// 连接中断
        /// </summary>
        CONNET_ERROT,
        /// <summary>
        /// 收到消息
        /// </summary>
        CONNET_MSG
    }
    public class SocketTcp : EventDispatch<NetEvent>
    {
        /// <summary>
        /// 头部字节
        /// </summary>
        public const int head_size = 8;
        /// <summary>
        /// 是否使用小端
        /// </summary>
        public const bool IsLittleEndian = true;
        bool _activity = false;
        /// <summary>
        /// 是否已启用
        /// </summary>
        public bool Activity
        {
            get { return _activity; }
        }
        /// <summary>
        /// 接受的消息,队列
        /// </summary>
        private Queue<BucketItem> MsgList;
        private Queue<NetEvent> codeMsg;
        public int port { get; private set; }
        public string ip { get; private set; }
        Socket socket;
        SocketAsyncEventArgs ReceiveSaea;
        SocketAsyncEventArgs sendSaea;
        byte[] sendData;
        /// <summary>
        /// 最大允许单个数据包30720字节
        /// 
        /// </summary>
        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<NetEvent>();
            MsgList = new Queue<BucketItem>();
        }
        private SocketAsyncEventArgs connetedSaea;
        /// <summary>
        /// 发起链接请求
        /// 会调度CONNETED或CONNET_IOERROT事件
        /// </summary>
        public void Start()
        {
            if (socket==null)
            {
                try
                {
                    socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                    connetedSaea = new SocketAsyncEventArgs();
                    connetedSaea.Completed += new EventHandler<SocketAsyncEventArgs>(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<SocketAsyncEventArgs>(ReceiveCall);//设置回调方法
            //codeMsg.Enqueue(1);
            //连接后开始从服务器读取网络消息
            socket.ReceiveAsync(ReceiveSaea);
        }
        int fragmentLen;
        byte[] fragment;
        /// <summary>
        /// 回调读取网络数据、检查是否断线。
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        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);
                Queue<BucketItem> arr = 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;
            }
        }
        /// <summary>
        /// 发送字节流
        /// </summary>
        /// <param name="data"></param>
        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);
        }
        /// <summary>
        /// 主线程每帧调用以拿取数据
        /// </summary>
        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;
            }
        }
    }
}

评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值