独立项目-Socket通讯 客户端代码-03

#region 模块信息
// **********************************************************************
// Copyright (C) 2018 The company name
//
// 文件名(File Name):             NetWorkSocket.cs
// 作者(Author):                  Dean1874
// 创建时间(CreateTime):          2018-06-06 16:08:08
// 修改者列表(modifier):
// 模块描述(Module description):
// 
// **********************************************************************
#endregion

using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using UnityEngine;

public class NetWorkSocket : DontDesMonoSingleton<NetWorkSocket> 
{
    //客户端Socket
    private Socket m_Client;

    //消息缓冲区
    //private byte[] m_Buffer = new byte[10240];

    #region 接收数据所需要的属性

    //接收数据包的缓冲区
    private byte[] m_ReceiveBuffer = new byte[10240];

    //接收数据的缓存数据流
    private MMO_MemoryStream m_ReceiveMS = new MMO_MemoryStream();

    //接收消息队列
    private Queue<byte[]> m_ReceiveQueue = new Queue<byte[]>();

    private int m_ReceiveCount = 0;
    #endregion

    #region 发送消息所需要的属性
    //发送消息队列
    private Queue<byte[]> m_SendQueue = new Queue<byte[]>();

    //检查队列的委托
    private Action m_CheckSendQueue;

    #endregion 

    #region Connect 连接到Socket服务器

    /// <summary>
    /// 连接到Socket服务器
    /// </summary>
    /// <param name="_ip">ip</param>
    /// <param name="_port">端口号</param>
    public void Connect(string _ip, int _port)
    {
        //当客户端Socket不为空,并且处于连接中状态
        if (m_Client != null && m_Client.Connected)
        {
            return;
        }

        m_Client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

        //尝试连接
        try
        {
            m_Client.Connect(new IPEndPoint(IPAddress.Parse(_ip), _port));
            Debug.Log("连接成功!");

            m_CheckSendQueue = OnCheckSendQueueCallBack;

            //接收消息
            ReceiveMsg();
        }
        catch (Exception ex)
        {
            Debug.Log("连接失败= " + ex.Message);
        }
    }
    #endregion 

    //====================================发送消息========================================
    #region MakeData 封装数据包
    /// <summary>
    /// 封装数据包
    /// </summary>
    /// <param name="_data">需要封装的数据</param>
    /// <returns></returns>
    private byte[] MakeData(byte[] _data)
    {
        byte[] retData = null;

        using (MMO_MemoryStream ms = new MMO_MemoryStream())
        {
            ms.WriteUShort((ushort)_data.Length);
            ms.Write(_data, 0, _data.Length);

            retData = ms.ToArray();
        }

        return retData;
    }
    #endregion

    #region SendMsg 发送消息 --- 把消息加入队列
    /// <summary>
    /// 发送消息
    /// </summary>
    /// <param name="_data"></param>
    public void SendMsg(byte[] _data)
    {
        //得到封装后的数据包
        byte[] sendBuffer = MakeData(_data);

        //加锁
        lock (m_SendQueue)
        {
            //把数据包添加到消息队列中
            m_SendQueue.Enqueue(sendBuffer);

            if (m_CheckSendQueue != null)
            {
                //启动委托(执行委托)
                m_CheckSendQueue.BeginInvoke(null, null);
            }
        }
    }
    #endregion

    #region OnCheckSendQueueCallBack 检查队列的委托回调
    /// <summary>
    /// 检查队列的委托回调
    /// </summary>
    private void OnCheckSendQueueCallBack()
    {
        //加锁
        lock (m_SendQueue)
        {
            //如果队列中有数据包,则发送数据包
            if (m_SendQueue.Count > 0)
            {
                //发送数据包
                Send(m_SendQueue.Dequeue());
            }
        }
    }
    #endregion

    #region Send 真正发送数据包到服务器
    /// <summary>
    /// 真正发送数据包到服务器
    /// </summary>
    /// <param name="_buffer"></param>
    private void Send(byte[] _buffer)
    {
        m_Client.BeginSend(_buffer, 0, _buffer.Length, SocketFlags.None, SendCallBack, m_Client);
    }
    #endregion

    #region SendCallBack 发送数据包的回调
    /// <summary>
    /// 发送数据包的回调
    /// </summary>
    /// <param name="ar"></param>
    private void SendCallBack(IAsyncResult ar)
    {
        m_Client.EndSend(ar);

        //继续检查队列
        OnCheckSendQueueCallBack();
    }
    #endregion

    //====================================接收消息========================================

    #region Update 每帧取5个消息
    private void Update()
    {
        //从队列中每帧取5个数据
        while (true)
        {
            if (m_ReceiveCount <= 5)
            {
                m_ReceiveCount++;
                lock (m_ReceiveQueue)
                {
                    if (m_ReceiveQueue.Count > 0)
                    {
                        byte[] buffer = m_ReceiveQueue.Dequeue();

                        using (MMO_MemoryStream ms = new MMO_MemoryStream(buffer))
                        {
                            string msg = ms.ReadUTF8String();
                            Debug.Log(msg);
                        }

                        ////temp 临时处理
                        //using (MMO_MemoryStream ms2 = new MMO_MemoryStream())
                        //{
                        //    ms2.WriteUTF8String("客户端时间:" + DateTime.Now.ToString());

                        //    this.SendMsg(ms2.ToArray());
                        //}


                    }
                    else
                    {
                        break;
                    }
                }
            }
            else
            {
                m_ReceiveCount = 0;
                break;
            }
        }
    }
    #endregion

    #region ReceiveMsg 接收数据
    /// <summary>
    /// 接收数据
    /// </summary>
    private void ReceiveMsg()
    {
        //异步接收数据
        m_Client.BeginReceive(m_ReceiveBuffer, 0, m_ReceiveBuffer.Length, SocketFlags.None, ReceiveCallBack, m_Client);
    }
    #endregion

    #region ReceiveCallBack 接收数据回调
    //接收数据回调
    private void ReceiveCallBack(IAsyncResult ar)
    {
        try
        {
            //接收数据的长度
            int len = m_Client.EndReceive(ar);

            //已经接收到数据
            if (len > 0)
            {
                //指定接收到的数据 写在缓冲数据流的尾部
                m_ReceiveMS.Position = m_ReceiveMS.Length;

                //把指定长度的字节 写入数据流
                m_ReceiveMS.Write(m_ReceiveBuffer, 0, len);

                //如果缓存数据流的长度 > 2,则说明至少有个不完整的包过来了
                //注意:为什么是大于2?
                //因为:我们客户端封装数据包的时候 用的是ushort 长度就是2
                if (m_ReceiveMS.Length > 2)
                {
                    while (true)
                    {
                        //把指针位置放在0处
                        m_ReceiveMS.Position = 0;

                        //读取到包体的长度
                        int currMsgLen = m_ReceiveMS.ReadUShort();

                        //得到总包的长度 = 包头长度 + 包体长度
                        int currFullMsgLen = 2 + currMsgLen;

                        //如果数据流的长度 大于等于 整包的长度
                        //说明至少收到了一个完整的包
                        if (m_ReceiveMS.Length >= currFullMsgLen)
                        {
                            //定义包体的byte[]数组
                            byte[] buffer = new byte[currMsgLen];

                            //把数据流指针放在2的位置
                            //也就是包体的位置
                            m_ReceiveMS.Position = 2;

                            //把包体读到byte数组中
                            m_ReceiveMS.Read(buffer, 0, currMsgLen);                           

                            //加锁
                            lock (m_ReceiveQueue)
                            {
                                //把收到的数据放入队列中
                                m_ReceiveQueue.Enqueue(buffer);
                            }

                            //===============处理剩余字节数组==========================

                            //剩余字节长度
                            int remainLen = (int)m_ReceiveMS.Length - currFullMsgLen;

                            //有剩余字节
                            if (remainLen > 0)
                            {
                                //把指针放在第一个包的尾部
                                m_ReceiveMS.Position = currFullMsgLen;

                                //定义剩余字节数组
                                byte[] remainBuffer = new byte[remainLen];

                                //把数据流读到剩余字节数组里
                                m_ReceiveMS.Read(remainBuffer, 0, remainLen);

                                //清空数据流
                                m_ReceiveMS.Position = 0;
                                m_ReceiveMS.SetLength(0);

                                //把剩余的字节数组重新写入数据流
                                m_ReceiveMS.Write(remainBuffer, 0, remainBuffer.Length);

                                remainBuffer = null;
                            }
                            //没有剩余字节
                            else
                            {
                                //清空数据流
                                m_ReceiveMS.Position = 0;
                                m_ReceiveMS.SetLength(0);

                                break;
                            }
                        }
                        //还没有收到完整包
                        else
                        {
                            break;
                        }
                    }
                }
                //进行下一次接收数据包
                ReceiveMsg();

            }
            //未接收到数据
            else
            {
                //服务器断开连接
                Debug.Log(string.Format("服务器 {0} 断开连接!", m_Client.RemoteEndPoint.ToString()));
            }
        }
        catch (Exception ex)
        {
            //服务器断开连接
            Debug.Log(string.Format("服务器 {0} 断开连接!", m_Client.RemoteEndPoint.ToString()));
        }
    }
    #endregion

    #region OnDestroy 销毁的时候
    /// <summary>
    /// 销毁的时候
    /// </summary>
    private void OnDestroy()
    {
        if (m_Client != null && m_Client.Connected)
        {
            m_Client.Shutdown(SocketShutdown.Both);
            m_Client.Close();
        }
    }
    #endregion
}
原文地址:https://www.cnblogs.com/Dean27/p/9152193.html