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