Seven is the number.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

368 lines
13 KiB

3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
  1. #if UNITY_WEBGL || WEBSOCKET || ((UNITY_XBOXONE || UNITY_GAMECORE) && UNITY_EDITOR)
  2. // --------------------------------------------------------------------------------------------------------------------
  3. // <copyright file="SocketWebTcp.cs" company="Exit Games GmbH">
  4. // Copyright (c) Exit Games GmbH. All rights reserved.
  5. // </copyright>
  6. // <summary>
  7. // Internal class to encapsulate the network i/o functionality for the realtime library.
  8. // </summary>
  9. // <author>developer@exitgames.com</author>
  10. // --------------------------------------------------------------------------------------------------------------------
  11. namespace ExitGames.Client.Photon
  12. {
  13. using System;
  14. using System.Collections;
  15. using UnityEngine;
  16. using SupportClassPun = ExitGames.Client.Photon.SupportClass;
  17. #if !(UNITY_WEBGL || NETFX_CORE)
  18. using System.Net;
  19. using System.Net.Sockets;
  20. using System.Threading;
  21. #endif
  22. /// <summary>
  23. /// Yield Instruction to Wait for real seconds. Very important to keep connection working if Time.TimeScale is altered, we still want accurate network events
  24. /// </summary>
  25. public sealed class WaitForRealSeconds : CustomYieldInstruction
  26. {
  27. private readonly float _endTime;
  28. public override bool keepWaiting
  29. {
  30. get { return this._endTime > Time.realtimeSinceStartup; }
  31. }
  32. public WaitForRealSeconds(float seconds)
  33. {
  34. this._endTime = Time.realtimeSinceStartup + seconds;
  35. }
  36. }
  37. /// <summary>
  38. /// Internal class to encapsulate the network i/o functionality for the realtime libary.
  39. /// </summary>
  40. public class SocketWebTcp : IPhotonSocket, IDisposable
  41. {
  42. private WebSocket sock;
  43. private readonly object syncer = new object();
  44. public SocketWebTcp(PeerBase npeer) : base(npeer)
  45. {
  46. this.ServerAddress = npeer.ServerAddress;
  47. if (this.ReportDebugOfLevel(DebugLevel.INFO))
  48. {
  49. this.Listener.DebugReturn(DebugLevel.INFO, "new SocketWebTcp() for Unity. Server: " + this.ServerAddress);
  50. }
  51. //this.Protocol = ConnectionProtocol.WebSocket;
  52. this.PollReceive = false;
  53. }
  54. public void Dispose()
  55. {
  56. this.State = PhotonSocketState.Disconnecting;
  57. if (this.sock != null)
  58. {
  59. try
  60. {
  61. if (this.sock.Connected) this.sock.Close();
  62. }
  63. catch (Exception ex)
  64. {
  65. this.EnqueueDebugReturn(DebugLevel.INFO, "Exception in Dispose(): " + ex);
  66. }
  67. }
  68. this.sock = null;
  69. this.State = PhotonSocketState.Disconnected;
  70. }
  71. GameObject websocketConnectionObject;
  72. public override bool Connect()
  73. {
  74. //bool baseOk = base.Connect();
  75. //if (!baseOk)
  76. //{
  77. // return false;
  78. //}
  79. this.State = PhotonSocketState.Connecting;
  80. if (this.websocketConnectionObject != null)
  81. {
  82. UnityEngine.Object.Destroy(this.websocketConnectionObject);
  83. }
  84. this.websocketConnectionObject = new GameObject("websocketConnectionObject");
  85. MonoBehaviour mb = this.websocketConnectionObject.AddComponent<MonoBehaviourExt>();
  86. this.websocketConnectionObject.hideFlags = HideFlags.HideInHierarchy;
  87. UnityEngine.Object.DontDestroyOnLoad(this.websocketConnectionObject);
  88. #if UNITY_WEBGL || NETFX_CORE
  89. this.sock = new WebSocket(new Uri(this.ConnectAddress), this.SerializationProtocol);
  90. this.sock.Connect();
  91. mb.StartCoroutine(this.ReceiveLoop());
  92. #else
  93. mb.StartCoroutine(this.DetectIpVersionAndConnect(mb));
  94. #endif
  95. return true;
  96. }
  97. #if !(UNITY_WEBGL || NETFX_CORE)
  98. private bool ipVersionDetectDone;
  99. private IEnumerator DetectIpVersionAndConnect(MonoBehaviour mb)
  100. {
  101. Uri uri = null;
  102. try
  103. {
  104. uri = new Uri(this.ConnectAddress);
  105. }
  106. catch (Exception ex)
  107. {
  108. if (this.ReportDebugOfLevel(DebugLevel.ERROR))
  109. {
  110. this.Listener.DebugReturn(DebugLevel.ERROR, "Failed to create a URI from ConnectAddress (" + ConnectAddress + "). Exception: " + ex);
  111. }
  112. }
  113. if (uri != null && uri.HostNameType == UriHostNameType.Dns)
  114. {
  115. ipVersionDetectDone = false;
  116. ThreadPool.QueueUserWorkItem(this.DetectIpVersion, uri.Host);
  117. while (!this.ipVersionDetectDone)
  118. {
  119. yield return new WaitForRealSeconds(0.1f);
  120. }
  121. }
  122. if (this.AddressResolvedAsIpv6)
  123. {
  124. this.ConnectAddress += "&IPv6";
  125. }
  126. if (this.ReportDebugOfLevel(DebugLevel.INFO))
  127. {
  128. this.Listener.DebugReturn(DebugLevel.INFO, "DetectIpVersionAndConnect() AddressResolvedAsIpv6: " + this.AddressResolvedAsIpv6 + " ConnectAddress: " + ConnectAddress);
  129. }
  130. this.sock = new WebSocket(new Uri(this.ConnectAddress), this.SerializationProtocol);
  131. this.sock.Connect();
  132. mb.StartCoroutine(this.ReceiveLoop());
  133. }
  134. // state has to be the hostname string
  135. private void DetectIpVersion(object state)
  136. {
  137. string host = state as string;
  138. IPAddress[] ipAddresses;
  139. try
  140. {
  141. ipAddresses = Dns.GetHostAddresses(host);
  142. foreach (IPAddress ipAddress in ipAddresses)
  143. {
  144. if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6)
  145. {
  146. this.AddressResolvedAsIpv6 = true;
  147. break;
  148. }
  149. }
  150. }
  151. catch (Exception ex)
  152. {
  153. this.Listener.DebugReturn(DebugLevel.INFO, "DetectIpVersionAndConnect (uri: " + host + "= thread failed: " + ex);
  154. }
  155. this.ipVersionDetectDone = true;
  156. }
  157. #endif
  158. public override bool Disconnect()
  159. {
  160. if (this.ReportDebugOfLevel(DebugLevel.INFO))
  161. {
  162. this.Listener.DebugReturn(DebugLevel.INFO, "SocketWebTcp.Disconnect()");
  163. }
  164. this.State = PhotonSocketState.Disconnecting;
  165. lock (this.syncer)
  166. {
  167. if (this.sock != null)
  168. {
  169. try
  170. {
  171. this.sock.Close();
  172. }
  173. catch (Exception ex)
  174. {
  175. this.Listener.DebugReturn(DebugLevel.ERROR, "Exception in Disconnect(): " + ex);
  176. }
  177. this.sock = null;
  178. }
  179. }
  180. if (this.websocketConnectionObject != null)
  181. {
  182. UnityEngine.Object.Destroy(this.websocketConnectionObject);
  183. }
  184. this.State = PhotonSocketState.Disconnected;
  185. return true;
  186. }
  187. /// <summary>
  188. /// used by TPeer*
  189. /// </summary>
  190. public override PhotonSocketError Send(byte[] data, int length)
  191. {
  192. if (this.State != PhotonSocketState.Connected)
  193. {
  194. return PhotonSocketError.Skipped;
  195. }
  196. try
  197. {
  198. if (data.Length > length)
  199. {
  200. byte[] trimmedData = new byte[length];
  201. Buffer.BlockCopy(data, 0, trimmedData, 0, length);
  202. data = trimmedData;
  203. }
  204. if (this.ReportDebugOfLevel(DebugLevel.ALL))
  205. {
  206. this.Listener.DebugReturn(DebugLevel.ALL, "Sending: " + SupportClassPun.ByteArrayToString(data));
  207. }
  208. if (this.sock != null)
  209. {
  210. this.sock.Send(data);
  211. }
  212. }
  213. catch (Exception e)
  214. {
  215. this.Listener.DebugReturn(DebugLevel.ERROR, "Cannot send to: " + this.ServerAddress + ". " + e.Message);
  216. this.HandleException(StatusCode.Exception);
  217. return PhotonSocketError.Exception;
  218. }
  219. return PhotonSocketError.Success;
  220. }
  221. public override PhotonSocketError Receive(out byte[] data)
  222. {
  223. data = null;
  224. return PhotonSocketError.NoData;
  225. }
  226. internal const int ALL_HEADER_BYTES = 9;
  227. internal const int TCP_HEADER_BYTES = 7;
  228. internal const int MSG_HEADER_BYTES = 2;
  229. public IEnumerator ReceiveLoop()
  230. {
  231. //this.Listener.DebugReturn(DebugLevel.INFO, "ReceiveLoop()");
  232. if (this.sock != null)
  233. {
  234. while (this.sock != null && !this.sock.Connected && this.sock.Error == null)
  235. {
  236. yield return new WaitForRealSeconds(0.1f);
  237. }
  238. if (this.sock != null)
  239. {
  240. if (this.sock.Error != null)
  241. {
  242. this.Listener.DebugReturn(DebugLevel.ERROR, "Exiting receive thread. Server: " + this.ServerAddress + ":" + this.ServerPort + " Error: " + this.sock.Error);
  243. this.HandleException(StatusCode.ExceptionOnConnect);
  244. }
  245. else
  246. {
  247. // connected
  248. if (this.ReportDebugOfLevel(DebugLevel.ALL))
  249. {
  250. this.Listener.DebugReturn(DebugLevel.ALL, "Receiving by websocket. this.State: " + this.State);
  251. }
  252. this.State = PhotonSocketState.Connected;
  253. this.peerBase.OnConnect();
  254. while (this.State == PhotonSocketState.Connected)
  255. {
  256. if (this.sock != null)
  257. {
  258. if (this.sock.Error != null)
  259. {
  260. this.Listener.DebugReturn(DebugLevel.ERROR, "Exiting receive thread (inside loop). Server: " + this.ServerAddress + ":" + this.ServerPort + " Error: " + this.sock.Error);
  261. this.HandleException(StatusCode.ExceptionOnReceive);
  262. break;
  263. }
  264. else
  265. {
  266. byte[] inBuff = this.sock.Recv();
  267. if (inBuff == null || inBuff.Length == 0)
  268. {
  269. // nothing received. wait a bit, try again
  270. yield return new WaitForRealSeconds(0.02f);
  271. continue;
  272. }
  273. if (this.ReportDebugOfLevel(DebugLevel.ALL))
  274. {
  275. this.Listener.DebugReturn(DebugLevel.ALL, "TCP << " + inBuff.Length + " = " + SupportClassPun.ByteArrayToString(inBuff));
  276. }
  277. if (inBuff.Length > 0)
  278. {
  279. try
  280. {
  281. this.HandleReceivedDatagram(inBuff, inBuff.Length, false);
  282. }
  283. catch (Exception e)
  284. {
  285. if (this.State != PhotonSocketState.Disconnecting && this.State != PhotonSocketState.Disconnected)
  286. {
  287. if (this.ReportDebugOfLevel(DebugLevel.ERROR))
  288. {
  289. this.EnqueueDebugReturn(DebugLevel.ERROR, "Receive issue. State: " + this.State + ". Server: '" + this.ServerAddress + "' Exception: " + e);
  290. }
  291. this.HandleException(StatusCode.ExceptionOnReceive);
  292. }
  293. }
  294. }
  295. }
  296. }
  297. }
  298. }
  299. }
  300. }
  301. this.Disconnect();
  302. }
  303. private class MonoBehaviourExt : MonoBehaviour { }
  304. }
  305. }
  306. #endif