NetworkSession.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. /****************************************************************************
  2. * Copyright 2019 Nreal Techonology Limited. All rights reserved.
  3. *
  4. * This file is part of NRSDK.
  5. *
  6. * https://www.nreal.ai/
  7. *
  8. *****************************************************************************/
  9. namespace NRKernal.Experimental.NetWork
  10. {
  11. using System;
  12. using System.Collections;
  13. using System.Collections.Generic;
  14. using System.IO;
  15. using System.Net;
  16. using System.Net.Sockets;
  17. using System.Text;
  18. using UnityEngine;
  19. /// <summary> Call back. </summary>
  20. /// <param name="data"> The data.</param>
  21. public delegate void CallBack(byte[] data);
  22. /// <summary> A network session. </summary>
  23. public static class NetworkSession
  24. {
  25. /// <summary> Clent connect state. </summary>
  26. private enum ClientState
  27. {
  28. /// <summary> Disconnect. </summary>
  29. None,
  30. /// <summary> Connect server success. </summary>
  31. Connected,
  32. }
  33. /// <summary> Message type dictionary. </summary>
  34. private static Dictionary<MessageType, CallBack> m_CallBacks = new Dictionary<MessageType, CallBack>();
  35. /// <summary> Message queue. </summary>
  36. private static Queue<byte[]> m_Messages;
  37. /// <summary> Client current state. </summary>
  38. private static ClientState m_CurState;
  39. /// <summary> Gets or sets the current state. </summary>
  40. /// <value> The current state. </value>
  41. private static ClientState CurState
  42. {
  43. get
  44. {
  45. return m_CurState;
  46. }
  47. set
  48. {
  49. m_CurState = value;
  50. if (m_CurState == ClientState.Connected)
  51. {
  52. CallBack callback;
  53. if (m_CallBacks.TryGetValue(MessageType.Connected, out callback))
  54. {
  55. callback?.Invoke(null);
  56. }
  57. }
  58. else
  59. {
  60. CallBack callback;
  61. if (m_CallBacks.TryGetValue(MessageType.Disconnect, out callback))
  62. {
  63. callback?.Invoke(null);
  64. }
  65. }
  66. }
  67. }
  68. /// <summary> The client. </summary>
  69. private static TcpClient m_Client;
  70. /// <summary> The stream. </summary>
  71. private static NetworkStream m_Stream;
  72. /// <summary> Target address. </summary>
  73. private static IPAddress m_Address;
  74. /// <summary> The port. </summary>
  75. private static int m_Port;
  76. /// <summary> Heart beat time stamp. </summary>
  77. private const float HEARTBEAT_TIME = 1;
  78. /// <summary> Time from last heart beat package. </summary>
  79. private static float m_Timer = HEARTBEAT_TIME;
  80. /// <summary> Get heart beat msg from server. </summary>
  81. public static bool Received = true;
  82. #region coroutines
  83. /// <summary> Connects the coroutine. </summary>
  84. /// <returns> An IEnumerator. </returns>
  85. private static IEnumerator ConnectCoroutine()
  86. {
  87. m_Client = new TcpClient();
  88. IAsyncResult async = m_Client.BeginConnect(m_Address, m_Port, null, null);
  89. while (!async.IsCompleted)
  90. {
  91. NRDebugger.Info("Contecting server...");
  92. yield return null;
  93. }
  94. try
  95. {
  96. m_Client.EndConnect(async);
  97. }
  98. catch (Exception ex)
  99. {
  100. Debug.LogWarning("Conncet server faild :" + ex.Message);
  101. yield break;
  102. }
  103. // Get data stream
  104. try
  105. {
  106. m_Stream = m_Client.GetStream();
  107. }
  108. catch (Exception ex)
  109. {
  110. Debug.LogWarning("Connect server faild:" + ex.Message);
  111. yield break;
  112. }
  113. if (m_Stream == null)
  114. {
  115. Debug.LogWarning("Connect server faild: data stream is empty");
  116. yield break;
  117. }
  118. CurState = ClientState.Connected;
  119. m_Messages = new Queue<byte[]>();
  120. NRDebugger.Info("Connect server success.");
  121. // Set asyn msg send
  122. NetworkCoroutine.Instance.StartCoroutine(HeartBeat());
  123. // Set asyn msg receive
  124. NetworkCoroutine.Instance.StartCoroutine(ReceiveCoroutine());
  125. // Set quit event
  126. NetworkCoroutine.Instance.SetQuitEvent(() => { m_Client.Close(); CurState = ClientState.None; });
  127. }
  128. /// <summary> Heart beat. </summary>
  129. /// <returns> An IEnumerator. </returns>
  130. private static IEnumerator HeartBeat()
  131. {
  132. while (CurState == ClientState.Connected)
  133. {
  134. m_Timer += Time.deltaTime;
  135. if (m_Messages.Count > 0)
  136. {
  137. byte[] data = m_Messages.Dequeue();
  138. yield return WriteCoroutine(data);
  139. }
  140. // Heart beat strategy
  141. if (m_Timer >= HEARTBEAT_TIME)
  142. {
  143. // if dont receive last heart beat package.
  144. if (!Received)
  145. {
  146. CurState = ClientState.None;
  147. Debug.LogWarning("Heart beat error. disconnect server.");
  148. yield break;
  149. }
  150. m_Timer = 0;
  151. byte[] data = Pack(MessageType.HeartBeat);
  152. yield return WriteCoroutine(data);
  153. NRDebugger.Debug("Send a heart beat package.");
  154. }
  155. yield return null;
  156. }
  157. }
  158. /// <summary> Receive coroutine. </summary>
  159. /// <returns> An IEnumerator. </returns>
  160. private static IEnumerator ReceiveCoroutine()
  161. {
  162. while (CurState == ClientState.Connected)
  163. {
  164. byte[] data = new byte[4];
  165. int length; // msg len
  166. MessageType type; // msg type
  167. int receive = 0; // receive len
  168. IAsyncResult async = m_Stream.BeginRead(data, 0, data.Length, null, null);
  169. while (!async.IsCompleted)
  170. {
  171. yield return null;
  172. }
  173. try
  174. {
  175. receive = m_Stream.EndRead(async);
  176. }
  177. catch (Exception ex)
  178. {
  179. CurState = ClientState.None;
  180. Debug.LogWarning("Receive msg package head erro:" + ex.Message);
  181. yield break;
  182. }
  183. if (receive < data.Length)
  184. {
  185. CurState = ClientState.None;
  186. Debug.LogWarning("Receive msg package head erro");
  187. yield break;
  188. }
  189. using (MemoryStream stream = new MemoryStream(data))
  190. {
  191. BinaryReader binary = new BinaryReader(stream, Encoding.UTF8); // parase data using UTF-8
  192. try
  193. {
  194. length = binary.ReadUInt16();
  195. type = (MessageType)binary.ReadUInt16();
  196. }
  197. catch (Exception)
  198. {
  199. CurState = ClientState.None;
  200. Debug.LogWarning("Receive msg package head erro");
  201. yield break;
  202. }
  203. }
  204. if (length - 4 > 0)
  205. {
  206. data = new byte[length - 4];
  207. async = m_Stream.BeginRead(data, 0, data.Length, null, null);
  208. while (!async.IsCompleted)
  209. {
  210. yield return null;
  211. }
  212. try
  213. {
  214. receive = m_Stream.EndRead(async);
  215. }
  216. catch (Exception ex)
  217. {
  218. CurState = ClientState.None;
  219. Debug.LogWarning("Receive msg package head erro:" + ex.Message);
  220. yield break;
  221. }
  222. if (receive < data.Length)
  223. {
  224. CurState = ClientState.None;
  225. Debug.LogWarning("Receive msg package head erro");
  226. yield break;
  227. }
  228. }
  229. else
  230. {
  231. data = new byte[0];
  232. receive = 0;
  233. }
  234. if (m_CallBacks.ContainsKey(type))
  235. {
  236. CallBack method = m_CallBacks[type];
  237. method(data);
  238. }
  239. else
  240. {
  241. NRDebugger.Warning("Did not regist the msg callback : " + type);
  242. }
  243. }
  244. }
  245. /// <summary> Writes a coroutine. </summary>
  246. /// <param name="data"> The data.</param>
  247. /// <returns> An IEnumerator. </returns>
  248. private static IEnumerator WriteCoroutine(byte[] data)
  249. {
  250. if (CurState != ClientState.Connected || m_Stream == null)
  251. {
  252. Debug.LogWarning("Connect error, can not receive msg");
  253. yield break;
  254. }
  255. IAsyncResult async = m_Stream.BeginWrite(data, 0, data.Length, null, null);
  256. while (!async.IsCompleted)
  257. {
  258. yield return null;
  259. }
  260. try
  261. {
  262. m_Stream.EndWrite(async);
  263. }
  264. catch (Exception ex)
  265. {
  266. CurState = ClientState.None;
  267. Debug.LogWarning("Send msg erro:" + ex.Message);
  268. }
  269. }
  270. #endregion
  271. /// <summary> Connect server. </summary>
  272. /// <param name="address"> (Optional) The address.</param>
  273. /// <param name="port"> (Optional) The port.</param>
  274. public static void Connect(string address = null, int port = 8848)
  275. {
  276. // Can not connect again after connected.
  277. if (CurState == ClientState.Connected)
  278. {
  279. NRDebugger.Info("Has connected server.");
  280. return;
  281. }
  282. if (address == null)
  283. address = NetworkUtils.GetLocalIPv4();
  284. // Cancle when get the ipaddress failed.
  285. if (!IPAddress.TryParse(address, out m_Address))
  286. {
  287. Debug.LogWarning("IP erro, try again.");
  288. return;
  289. }
  290. m_Port = port;
  291. // Connect service.
  292. NetworkCoroutine.Instance.StartCoroutine(ConnectCoroutine());
  293. }
  294. /// <summary> Closes this object. </summary>
  295. public static void Close()
  296. {
  297. if (CurState == ClientState.Connected)
  298. {
  299. m_Client.Close();
  300. m_CurState = ClientState.None;
  301. }
  302. NetworkCoroutine.Instance.StopAllCoroutines();
  303. }
  304. /// <summary> Regist callback event. </summary>
  305. /// <param name="type"> The type.</param>
  306. /// <param name="method"> The method.</param>
  307. public static void Register(MessageType type, CallBack method)
  308. {
  309. if (!m_CallBacks.ContainsKey(type))
  310. m_CallBacks.Add(type, method);
  311. else
  312. Debug.LogWarning("Regist the same msg type.");
  313. }
  314. /// <summary> Un register. </summary>
  315. /// <param name="type"> The type.</param>
  316. /// <param name="method"> The method.</param>
  317. public static void UnRegister(MessageType type, CallBack method)
  318. {
  319. if (m_CallBacks.ContainsKey(type))
  320. {
  321. m_CallBacks.Remove(type);
  322. }
  323. }
  324. /// <summary> Join the msg queue. </summary>
  325. /// <param name="type"> The type.</param>
  326. /// <param name="data"> (Optional) The data.</param>
  327. public static void Enqueue(MessageType type, byte[] data = null)
  328. {
  329. // Pack the data
  330. byte[] bytes = Pack(type, data);
  331. if (CurState == ClientState.Connected)
  332. {
  333. m_Messages.Enqueue(bytes);
  334. }
  335. }
  336. /// <summary> Pack the byte data. </summary>
  337. /// <param name="type"> The type.</param>
  338. /// <param name="data"> (Optional) The data.</param>
  339. /// <returns> A byte[]. </returns>
  340. private static byte[] Pack(MessageType type, byte[] data = null)
  341. {
  342. MessagePacker packer = new MessagePacker();
  343. if (data != null)
  344. {
  345. packer.Add((ushort)(4 + data.Length)); // msg length
  346. packer.Add((ushort)type); // msg type
  347. packer.Add(data); // msg content
  348. }
  349. else
  350. {
  351. packer.Add(4);
  352. packer.Add((ushort)type);
  353. }
  354. return packer.Package;
  355. }
  356. }
  357. }