独立项目-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 } }
Dean二十七