zoukankan      html  css  js  c++  java
  • 独立项目-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
        }
    }
  • 相关阅读:
    Mybatis 原始dao CRUD方法
    JQuery的焦点事件focus() 与按键事件keydown() 及js判断当前页面是否为顶级页面 子页面刷新将顶级页面刷新 window.top.location
    使用actionerror做失败登录验证
    Java项目中的下载 与 上传
    shiro框架 4种授权方式 说明
    javascript 中数组的创建 添加 与将数组转换成字符串 页面三种提交请求的方式
    序列化表单为json对象,datagrid带额外参提交一次查询 后台用Spring data JPA 实现带条件的分页查询 多表关联查询
    Spring data JPA 理解(默认查询 自定义查询 分页查询)及no session 三种处理方法
    orcal 数据库 maven架构 ssh框架 的全注解环境模版 maven中央仓库批量删除lastupdated文件后依然是lastupdated解决方法 mirror aliyun中央仓库
    EasyUI加zTree使用解析 easyui修改操作的表单回显方法 验证框提交表单前验证 datagrid的load方法
  • 原文地址:https://www.cnblogs.com/Dean27/p/9152203.html
Copyright © 2011-2022 走看看