独立项目-Socket通讯 服务器端代码-04

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using GameServerApp.Common;

namespace GameServerApp
{
    /// <summary>
    /// 客户端连接对象 负责和客户端进行通讯的
    /// </summary>
    public class ClientSocket
    {
        #region 属性
        //客户端Socket
        private Socket m_Socket;

        //当前ClientSocket所属角色
        private Role m_Role;
        #endregion

        #region 接收数据所需要的属性
        //接受数据线程
        private Thread m_ReceiveThread;

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

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

        #region 发送消息所需要的属性

        //发送消息队列
        private Queue<byte[]> m_SendQueue = new Queue<byte[]>();

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

        #endregion

        #region ClientSocket 构造函数
        public ClientSocket(Socket _socket, Role _role)
        {
            m_Socket = _socket;
            m_Role = _role;
            m_Role.Client_Socket = this;

            //启动线程,进行接收数据
            m_ReceiveThread = new Thread(ReceiveMsg);
            m_ReceiveThread.Start();

            m_CheckSendQueue = OnCheckSendQueueCallBack;

            //temp
            //using (MMO_MemoryStream ms = new MMO_MemoryStream())
            //{
            //    ms.WriteUTF8String(string.Format("欢迎登陆服务器!" + DateTime.Now.ToString()));

            //    this.SendMsg(ms.ToArray());
            //}
        }
        #endregion

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

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

        #region ReceiveCallBack 接收数据回调
        //接收数据回调
        private void ReceiveCallBack(IAsyncResult ar)
        {
            try
            {
                //接收数据的长度
                int len = m_Socket.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);
                                
                                //这个byte[]数组就是包体 也就是我们需要的数据
                                using (MMO_MemoryStream ms2 = new MMO_MemoryStream(buffer))
                                {
                                    string msg = ms2.ReadUTF8String();
                                    Console.WriteLine(msg);
                                }

                                ////temp 临时处理
                                //using (MMO_MemoryStream ms3 = new MMO_MemoryStream())
                                //{
                                //    ms3.WriteUTF8String(string.Format("服务器时间:" + DateTime.Now.ToString()));

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

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

                                //剩余字节长度
                                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
                {
                    //客户端断开连接
                    Console.WriteLine("客户端 {0} 断开连接!", m_Socket.RemoteEndPoint.ToString());

                    //移除角色管理组里面的角色
                    RoleManager.Instance.AllRole.Remove(m_Role);
                }
            }
            catch (Exception ex)
            {
                //客户端断开连接
                Console.WriteLine("客户端 {0} 断开连接!", m_Socket.RemoteEndPoint.ToString());

                //移除角色管理组里面的角色
                RoleManager.Instance.AllRole.Remove(m_Role);
            }
        }
        #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_Socket.BeginSend(_buffer, 0, _buffer.Length, SocketFlags.None, SendCallBack, m_Socket);
        }
        #endregion

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

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

 

posted @ 2018-06-07 18:31  Dean二十七  阅读(303)  评论(0编辑  收藏  举报