zoukankan      html  css  js  c++  java
  • 独立项目-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
    }
  • 相关阅读:
    PYTHON 中的字符集
    字符集--发展史
    循序渐进Python3(十三) --7--  django之models
    循序渐进Python3(十三) --8--  django之admin
    循序渐进Python3(十三) --6--  cookie和session
    循序渐进Python3(十三) --5-- django请求处理流程
    循序渐进Python3(十三) --4-- django之csrf使用
    循序渐进Python3(十三) --3-- django之form表单(为自动生成的html标签添加样式)
    循序渐进Python3(十三) --2-- django之form表单(自动生成html标签和自定制提示信息)
    循序渐进Python3(十三) --1-- django之form表单
  • 原文地址:https://www.cnblogs.com/Dean27/p/9152193.html
Copyright © 2011-2022 走看看