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.

399 lines
12 KiB

4 years ago
  1. using System;
  2. using System.Collections;
  3. using System.Threading;
  4. #if NETFX_CORE
  5. using System.Diagnostics;
  6. using Windows.Foundation;
  7. using Windows.Networking;
  8. using Windows.Networking.Sockets;
  9. using Windows.Storage.Streams;
  10. #endif
  11. #if !NO_SOCKET && !NETFX_CORE
  12. using System.Collections.Generic;
  13. using System.Diagnostics;
  14. using System.Net.Sockets;
  15. #endif
  16. namespace Photon.Realtime
  17. {
  18. public abstract class PhotonPing : IDisposable
  19. {
  20. public string DebugString = "";
  21. public bool Successful;
  22. protected internal bool GotResult;
  23. protected internal int PingLength = 13;
  24. protected internal byte[] PingBytes = new byte[] { 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x00 };
  25. protected internal byte PingId;
  26. private static readonly Random RandomIdProvider = new Random();
  27. public virtual bool StartPing(string ip)
  28. {
  29. throw new NotImplementedException();
  30. }
  31. public virtual bool Done()
  32. {
  33. throw new NotImplementedException();
  34. }
  35. public virtual void Dispose()
  36. {
  37. throw new NotImplementedException();
  38. }
  39. protected internal void Init()
  40. {
  41. this.GotResult = false;
  42. this.Successful = false;
  43. this.PingId = (byte)(RandomIdProvider.Next(255));
  44. }
  45. }
  46. #if !NETFX_CORE && !NO_SOCKET
  47. /// <summary>Uses C# Socket class from System.Net.Sockets (as Unity usually does).</summary>
  48. /// <remarks>Incompatible with Windows 8 Store/Phone API.</remarks>
  49. public class PingMono : PhotonPing
  50. {
  51. private Socket sock;
  52. /// <summary>
  53. /// Sends a "Photon Ping" to a server.
  54. /// </summary>
  55. /// <param name="ip">Address in IPv4 or IPv6 format. An address containing a '.' will be interpretet as IPv4.</param>
  56. /// <returns>True if the Photon Ping could be sent.</returns>
  57. public override bool StartPing(string ip)
  58. {
  59. this.Init();
  60. try
  61. {
  62. if (this.sock == null)
  63. {
  64. if (ip.Contains("."))
  65. {
  66. this.sock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
  67. }
  68. else
  69. {
  70. this.sock = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp);
  71. }
  72. this.sock.ReceiveTimeout = 5000;
  73. this.sock.Connect(ip, 5055);
  74. }
  75. this.PingBytes[this.PingBytes.Length - 1] = this.PingId;
  76. this.sock.Send(this.PingBytes);
  77. this.PingBytes[this.PingBytes.Length - 1] = (byte)(this.PingId+1); // invalidate the result, as we re-use the buffer
  78. }
  79. catch (Exception e)
  80. {
  81. this.sock = null;
  82. Console.WriteLine(e);
  83. }
  84. return false;
  85. }
  86. public override bool Done()
  87. {
  88. if (this.GotResult || this.sock == null)
  89. {
  90. return true;
  91. }
  92. if (!this.sock.Poll(0, SelectMode.SelectRead))
  93. {
  94. return false;
  95. }
  96. int read = this.sock.Receive(this.PingBytes, SocketFlags.None);
  97. bool replyMatch = this.PingBytes[this.PingBytes.Length - 1] == this.PingId && read == this.PingLength;
  98. if (!replyMatch)
  99. {
  100. this.DebugString += " ReplyMatch is false! ";
  101. }
  102. this.Successful = replyMatch;
  103. this.GotResult = true;
  104. return true;
  105. }
  106. public override void Dispose()
  107. {
  108. try
  109. {
  110. this.sock.Close();
  111. }
  112. catch
  113. {
  114. }
  115. this.sock = null;
  116. }
  117. }
  118. #endif
  119. #if NETFX_CORE
  120. /// <summary>Windows store API implementation of PhotonPing</summary>
  121. public class PingWindowsStore : PhotonPing
  122. {
  123. private DatagramSocket sock;
  124. private readonly object syncer = new object();
  125. public override bool StartPing(string host)
  126. {
  127. base.Init();
  128. EndpointPair endPoint = new EndpointPair(null, string.Empty, new HostName(host), "5055");
  129. this.sock = new DatagramSocket();
  130. this.sock.MessageReceived += OnMessageReceived;
  131. var result = this.sock.ConnectAsync(endPoint);
  132. result.Completed = this.OnConnected;
  133. this.DebugString += " End StartPing";
  134. return true;
  135. }
  136. public override bool Done()
  137. {
  138. return this.GotResult;
  139. }
  140. public override void Dispose()
  141. {
  142. this.sock = null;
  143. }
  144. private void OnConnected(IAsyncAction asyncinfo, AsyncStatus asyncstatus)
  145. {
  146. if (asyncinfo.AsTask().IsCompleted)
  147. {
  148. PingBytes[PingBytes.Length - 1] = PingId;
  149. DataWriter writer;
  150. writer = new DataWriter(sock.OutputStream);
  151. writer.WriteBytes(PingBytes);
  152. var res = writer.StoreAsync();
  153. res.AsTask().Wait(100);
  154. writer.DetachStream();
  155. writer.Dispose();
  156. PingBytes[PingBytes.Length - 1] = (byte)(PingId - 1);
  157. }
  158. else
  159. {
  160. // TODO: handle error
  161. }
  162. }
  163. private void OnMessageReceived(DatagramSocket sender, DatagramSocketMessageReceivedEventArgs args)
  164. {
  165. lock (syncer)
  166. {
  167. DataReader reader = null;
  168. try
  169. {
  170. reader = args.GetDataReader();
  171. uint receivedByteCount = reader.UnconsumedBufferLength;
  172. if (receivedByteCount > 0)
  173. {
  174. var resultBytes = new byte[receivedByteCount];
  175. reader.ReadBytes(resultBytes);
  176. //TODO: check result bytes!
  177. this.Successful = receivedByteCount == PingLength && resultBytes[resultBytes.Length - 1] == PingId;
  178. this.GotResult = true;
  179. }
  180. }
  181. catch
  182. {
  183. // TODO: handle error
  184. }
  185. }
  186. }
  187. }
  188. #endif
  189. #if NATIVE_SOCKETS
  190. /// <summary>Abstract base class to provide proper resource management for the below native ping implementations</summary>
  191. public abstract class PingNative : PhotonPing
  192. {
  193. // Native socket states - according to EnetConnect.h state definitions
  194. protected enum NativeSocketState : byte
  195. {
  196. Disconnected = 0,
  197. Connecting = 1,
  198. Connected = 2,
  199. ConnectionError = 3,
  200. SendError = 4,
  201. ReceiveError = 5,
  202. Disconnecting = 6
  203. }
  204. protected IntPtr pConnectionHandler = IntPtr.Zero;
  205. ~PingNative()
  206. {
  207. Dispose();
  208. }
  209. }
  210. /// <summary>Uses dynamic linked native Photon socket library via DllImport("PhotonSocketPlugin") attribute (as done by Unity Android and Unity PS3).</summary>
  211. public class PingNativeDynamic : PingNative
  212. {
  213. public override bool StartPing(string ip)
  214. {
  215. lock (SocketUdpNativeDynamic.syncer)
  216. {
  217. base.Init();
  218. if(pConnectionHandler == IntPtr.Zero)
  219. {
  220. pConnectionHandler = SocketUdpNativeDynamic.egconnect(ip);
  221. SocketUdpNativeDynamic.egservice(pConnectionHandler);
  222. byte state = SocketUdpNativeDynamic.eggetState(pConnectionHandler);
  223. while (state == (byte) NativeSocketState.Connecting)
  224. {
  225. SocketUdpNativeDynamic.egservice(pConnectionHandler);
  226. state = SocketUdpNativeDynamic.eggetState(pConnectionHandler);
  227. }
  228. }
  229. PingBytes[PingBytes.Length - 1] = PingId;
  230. SocketUdpNativeDynamic.egsend(pConnectionHandler, PingBytes, PingBytes.Length);
  231. SocketUdpNativeDynamic.egservice(pConnectionHandler);
  232. PingBytes[PingBytes.Length - 1] = (byte) (PingId - 1);
  233. return true;
  234. }
  235. }
  236. public override bool Done()
  237. {
  238. lock (SocketUdpNativeDynamic.syncer)
  239. {
  240. if (this.GotResult || pConnectionHandler == IntPtr.Zero)
  241. {
  242. return true;
  243. }
  244. int available = SocketUdpNativeDynamic.egservice(pConnectionHandler);
  245. if (available < PingLength)
  246. {
  247. return false;
  248. }
  249. int pingBytesLength = PingBytes.Length;
  250. int bytesInRemainginDatagrams = SocketUdpNativeDynamic.egread(pConnectionHandler, PingBytes, ref pingBytesLength);
  251. this.Successful = (PingBytes != null && PingBytes[PingBytes.Length - 1] == PingId);
  252. //Debug.Log("Successful: " + this.Successful + " bytesInRemainginDatagrams: " + bytesInRemainginDatagrams + " PingId: " + PingId);
  253. this.GotResult = true;
  254. return true;
  255. }
  256. }
  257. public override void Dispose()
  258. {
  259. lock (SocketUdpNativeDynamic.syncer)
  260. {
  261. if (this.pConnectionHandler != IntPtr.Zero)
  262. SocketUdpNativeDynamic.egdisconnect(this.pConnectionHandler);
  263. this.pConnectionHandler = IntPtr.Zero;
  264. }
  265. GC.SuppressFinalize(this);
  266. }
  267. }
  268. #if NATIVE_SOCKETS && NATIVE_SOCKETS_STATIC
  269. /// <summary>Uses static linked native Photon socket library via DllImport("__Internal") attribute (as done by Unity iOS and Unity Switch).</summary>
  270. public class PingNativeStatic : PingNative
  271. {
  272. public override bool StartPing(string ip)
  273. {
  274. base.Init();
  275. lock (SocketUdpNativeStatic.syncer)
  276. {
  277. if(pConnectionHandler == IntPtr.Zero)
  278. {
  279. pConnectionHandler = SocketUdpNativeStatic.egconnect(ip);
  280. SocketUdpNativeStatic.egservice(pConnectionHandler);
  281. byte state = SocketUdpNativeStatic.eggetState(pConnectionHandler);
  282. while (state == (byte) NativeSocketState.Connecting)
  283. {
  284. SocketUdpNativeStatic.egservice(pConnectionHandler);
  285. state = SocketUdpNativeStatic.eggetState(pConnectionHandler);
  286. Thread.Sleep(0); // suspending execution for a moment is critical on Switch for the OS to update the socket
  287. }
  288. }
  289. PingBytes[PingBytes.Length - 1] = PingId;
  290. SocketUdpNativeStatic.egsend(pConnectionHandler, PingBytes, PingBytes.Length);
  291. SocketUdpNativeStatic.egservice(pConnectionHandler);
  292. PingBytes[PingBytes.Length - 1] = (byte) (PingId - 1);
  293. return true;
  294. }
  295. }
  296. public override bool Done()
  297. {
  298. lock (SocketUdpNativeStatic.syncer)
  299. {
  300. if (this.GotResult || pConnectionHandler == IntPtr.Zero)
  301. {
  302. return true;
  303. }
  304. int available = SocketUdpNativeStatic.egservice(pConnectionHandler);
  305. if (available < PingLength)
  306. {
  307. return false;
  308. }
  309. int pingBytesLength = PingBytes.Length;
  310. int bytesInRemainginDatagrams = SocketUdpNativeStatic.egread(pConnectionHandler, PingBytes, ref pingBytesLength);
  311. this.Successful = (PingBytes != null && PingBytes[PingBytes.Length - 1] == PingId);
  312. //Debug.Log("Successful: " + this.Successful + " bytesInRemainginDatagrams: " + bytesInRemainginDatagrams + " PingId: " + PingId);
  313. this.GotResult = true;
  314. return true;
  315. }
  316. }
  317. public override void Dispose()
  318. {
  319. lock (SocketUdpNativeStatic.syncer)
  320. {
  321. if (pConnectionHandler != IntPtr.Zero)
  322. SocketUdpNativeStatic.egdisconnect(pConnectionHandler);
  323. pConnectionHandler = IntPtr.Zero;
  324. }
  325. GC.SuppressFinalize(this);
  326. }
  327. }
  328. #endif
  329. #endif
  330. }