ClientSocket.cs 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. using System;
  2. using System.Net.Sockets;
  3. using UnityEngine;
  4. using System.Collections.Generic;
  5. using System.IO;
  6. using System.Text;
  7. using System.Linq;
  8. using System.Threading.Tasks;
  9. namespace UnityCommon
  10. {
  11. public class ClientSocket
  12. {
  13. private Socket init()
  14. {
  15. Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
  16. // 接收的消息数据包大小限制为 0x4000 byte, 即16KB
  17. m_recvBuff = new byte[0x4000];
  18. m_recvCb = new AsyncCallback(RecvCallBack);
  19. return clientSocket;
  20. }
  21. /// <summary>
  22. /// 连接服务器
  23. /// </summary>
  24. /// <param name="host">ip地址</param>
  25. /// <param name="port">端口号</param>
  26. public void Connect(string host, int port, Action onFailed)
  27. {
  28. if (m_socket == null)
  29. m_socket = init();
  30. try
  31. {
  32. Debug.Log("connect: " + host + ":" + port);
  33. m_socket.SendTimeout = 3;
  34. m_socket.Connect(host, port);
  35. connected = true;
  36. }
  37. catch (Exception ex)
  38. {
  39. Debug.LogError(ex);
  40. onFailed?.Invoke();
  41. }
  42. }
  43. /// <summary>
  44. /// 发送消息
  45. /// </summary>
  46. public void SendData(byte[] bytes)
  47. {
  48. NetworkStream netstream = new NetworkStream(m_socket);
  49. netstream.Write(bytes, 0, bytes.Length);
  50. }
  51. /// <summary>
  52. /// 尝试接收消息(每帧调用)
  53. /// </summary>
  54. public void BeginReceive()
  55. {
  56. m_socket.BeginReceive(m_recvBuff, 0, m_recvBuff.Length, SocketFlags.None, m_recvCb, this);
  57. }
  58. /// <summary>
  59. /// 当收到服务器的消息时会回调这个函数
  60. /// </summary>
  61. private void RecvCallBack(IAsyncResult ar)
  62. {
  63. var len = m_socket.EndReceive(ar);
  64. byte[] msg = new byte[len];
  65. Array.Copy(m_recvBuff, msg, len);
  66. var msgStr = System.Text.Encoding.UTF8.GetString(msg);
  67. // 将消息塞入队列中
  68. m_msgQueue.Enqueue(msgStr);
  69. // 将buffer清零
  70. for (int i = 0; i < m_recvBuff.Length; ++i)
  71. {
  72. m_recvBuff[i] = 0;
  73. }
  74. }
  75. /// <summary>
  76. /// 从消息队列中取出消息
  77. /// </summary>
  78. /// <returns></returns>
  79. public string GetMsgFromQueue()
  80. {
  81. if (m_msgQueue.Count > 0)
  82. return m_msgQueue.Dequeue();
  83. return null;
  84. }
  85. /// <summary>
  86. /// 关闭Socket
  87. /// </summary>
  88. public void CloseSocket()
  89. {
  90. Debug.Log("close socket");
  91. try
  92. {
  93. m_socket.Shutdown(SocketShutdown.Both);
  94. m_socket.Close();
  95. }
  96. catch (Exception e)
  97. {
  98. //Debug.LogError(e);
  99. }
  100. finally
  101. {
  102. m_socket = null;
  103. connected = false;
  104. }
  105. }
  106. public bool connected = false;
  107. private byte[] m_recvBuff;
  108. private AsyncCallback m_recvCb;
  109. private Queue<string> m_msgQueue = new Queue<string>();
  110. private Socket m_socket;
  111. //public static void AcceptClient(Socket server)
  112. //{
  113. // Socket client = null;
  114. // do
  115. // {
  116. // try
  117. // {
  118. // client = server.Accept();
  119. // if (client != null)
  120. // {
  121. // Console.WriteLine("客户 \'{0}\' 已连接。", client.RemoteEndPoint.ToString());
  122. // Task.Run(() => ReceiveData(client)).Wait(100);
  123. // }
  124. // }
  125. // catch (Exception ex)
  126. // {
  127. // client = null;
  128. // }
  129. // } while (client != null);
  130. //}
  131. public static void ReceiveData(Socket socket) // 接收消息函数(传入一个socket对象)
  132. {
  133. try
  134. {
  135. MessageProtocol mp = null;
  136. int ReceiveLength = 0;
  137. byte[] staticReceiveBuffer = new byte[65536]; // 接收缓冲区(固定长度)
  138. byte[] dynamicReceiveBuffer = new byte[] { }; // 累加数据缓存(不定长)
  139. do
  140. {
  141. ReceiveLength = socket.Receive(staticReceiveBuffer); // 同步接收数据
  142. dynamicReceiveBuffer = CombineBytes(dynamicReceiveBuffer, 0, dynamicReceiveBuffer.Length, staticReceiveBuffer, 0, ReceiveLength); // 将之前多余的数据与接收的数据合并,形成一个完整的数据包
  143. if (ReceiveLength <= 0) // 如果接收到的数据长度小于0(通常表示socket已断开,但也不一定,需要进一步判断,此处可以忽略)
  144. {
  145. break; // 终止接收循环
  146. }
  147. else if (dynamicReceiveBuffer.Length < MessageProtocol.HEADLENGTH) // 如果缓存中的数据长度小于协议头长度,则继续接收
  148. {
  149. continue; // 跳过本次循环继续接收数据
  150. }
  151. else // 缓存中的数据大于等于协议头的长度(dynamicReadBuffer.Length >= 6)
  152. {
  153. var headInfo = MessageProtocol.GetHeadInfo(dynamicReceiveBuffer); // 解读协议头的信息
  154. while (dynamicReceiveBuffer.Length - MessageProtocol.HEADLENGTH >= headInfo.DataLength) // 当缓存数据长度减去协议头长度大于等于实际数据的长度则进入循环进行拆包处理
  155. {
  156. mp = new MessageProtocol(dynamicReceiveBuffer); // 拆包
  157. dynamicReceiveBuffer = mp.MoreData; // 将拆包后得出多余的字节付给缓存变量,以待下一次循环处理数据时使用,若下一次循环缓存数据长度不能构成一个完整的数据包则不进入循环跳到外层循环继续接收数据并将本次得出的多余数据与之合并重新拆包,依次循环。
  158. headInfo = MessageProtocol.GetHeadInfo(dynamicReceiveBuffer); // 从缓存中解读出下一次数据所需要的协议头信息,已准备下一次拆包循环,如果数据长度不能构成协议头所需的长度,拆包结果为0,下一次循环则不能成功进入,跳到外层循环继续接收数据合并缓存形成一个完整的数据包
  159. if (mp.Command == 0 && mp.Param == 0)
  160. {
  161. socket.Send(new MessageProtocol(0, 0, Encoding.UTF8.GetBytes("Goodbye\r\n再见!")).GetBytes());
  162. return;
  163. }
  164. ResponseRequest(socket, mp.GetMessage());
  165. } // 拆包循环结束
  166. }
  167. } while (ReceiveLength > 0);
  168. }
  169. finally
  170. {
  171. Console.WriteLine("客户 \'{0}\' 已离开", socket.RemoteEndPoint.ToString());
  172. socket.Close();
  173. }
  174. }
  175. public static void ResponseRequest(Socket client, (byte Command, byte Param, int DataLength, byte[] MessageData) message)
  176. {
  177. string text = Encoding.UTF8.GetString(message.MessageData);
  178. Console.WriteLine("客户 \'{0}\' 发来信息: \'{1}\'。", client.RemoteEndPoint.ToString(), text);
  179. text = new String(text.Reverse().ToArray());
  180. client.Send(new MessageProtocol(1, 1, Encoding.UTF8.GetBytes(text)).GetBytes());
  181. }
  182. public static byte[] CombineBytes(byte[] firstBytes, int firstIndex, int firstLength, byte[] secondBytes, int secondIndex, int secondLength)
  183. {
  184. byte[] bytes = null;
  185. MemoryStream ms = new MemoryStream();
  186. ms.Write(firstBytes, firstIndex, firstLength);
  187. ms.Write(secondBytes, secondIndex, secondLength);
  188. bytes = ms.ToArray();
  189. ms.Close();
  190. return (bytes);
  191. }
  192. }
  193. }