WebSocket.cs 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. #if UNITY_WEBGL || WEBSOCKET || WEBSOCKET_PROXYCONFIG
  2. // --------------------------------------------------------------------------------------------------------------------
  3. // <summary>
  4. // Provided originally by Unity to cover WebSocket support in WebGL and the Editor. Modified by Exit Games GmbH.
  5. // </summary>
  6. // <author>developer@exitgames.com</author>
  7. // --------------------------------------------------------------------------------------------------------------------
  8. namespace ExitGames.Client.Photon
  9. {
  10. using System;
  11. using System.Text;
  12. using ExitGames.Client.Photon;
  13. #if UNITY_WEBGL && !UNITY_EDITOR
  14. using System.Runtime.InteropServices;
  15. #else
  16. using WebSocketSharp;
  17. using System.Collections.Generic;
  18. using System.Security.Authentication;
  19. #endif
  20. public class WebSocket
  21. {
  22. private Uri mUrl;
  23. private string mProxyAddress;
  24. /// <summary>Photon uses this to agree on a serialization protocol. Either: GpBinaryV16 or GpBinaryV18. Based on enum SerializationProtocol.</summary>
  25. private string protocols = "GpBinaryV16";
  26. public Action<DebugLevel, string> DebugReturn { get; set; }
  27. public WebSocket(Uri url, string proxyAddress, string protocols = null)
  28. {
  29. this.mUrl = url;
  30. this.mProxyAddress = proxyAddress;
  31. if (protocols != null)
  32. {
  33. this.protocols = protocols;
  34. }
  35. string protocol = mUrl.Scheme;
  36. if (!protocol.Equals("ws") && !protocol.Equals("wss"))
  37. {
  38. throw new ArgumentException("Unsupported protocol: " + protocol);
  39. }
  40. }
  41. public string ProxyAddress
  42. {
  43. get { return mProxyAddress; }
  44. }
  45. public void SendString(string str)
  46. {
  47. Send(Encoding.UTF8.GetBytes(str));
  48. }
  49. public string RecvString()
  50. {
  51. byte[] retval = Recv();
  52. if (retval == null)
  53. return null;
  54. return Encoding.UTF8.GetString(retval);
  55. }
  56. #if UNITY_WEBGL && !UNITY_EDITOR
  57. [DllImport("__Internal")]
  58. private static extern int SocketCreate (string url, string protocols);
  59. [DllImport("__Internal")]
  60. private static extern int SocketState (int socketInstance);
  61. [DllImport("__Internal")]
  62. private static extern void SocketSend (int socketInstance, byte[] ptr, int length);
  63. [DllImport("__Internal")]
  64. private static extern void SocketRecv (int socketInstance, byte[] ptr, int length);
  65. [DllImport("__Internal")]
  66. private static extern int SocketRecvLength (int socketInstance);
  67. [DllImport("__Internal")]
  68. private static extern void SocketClose (int socketInstance);
  69. [DllImport("__Internal")]
  70. private static extern int SocketError (int socketInstance, byte[] ptr, int length);
  71. int m_NativeRef = 0;
  72. public void Send(byte[] buffer)
  73. {
  74. SocketSend (m_NativeRef, buffer, buffer.Length);
  75. }
  76. public byte[] Recv()
  77. {
  78. int length = SocketRecvLength (m_NativeRef);
  79. if (length == 0)
  80. return null;
  81. byte[] buffer = new byte[length];
  82. SocketRecv (m_NativeRef, buffer, length);
  83. return buffer;
  84. }
  85. public void Connect()
  86. {
  87. m_NativeRef = SocketCreate (mUrl.ToString(), this.protocols);
  88. //while (SocketState(m_NativeRef) == 0)
  89. // yield return 0;
  90. }
  91. public void Close()
  92. {
  93. SocketClose(m_NativeRef);
  94. }
  95. public bool Connected
  96. {
  97. get { return SocketState(m_NativeRef) != 0; }
  98. }
  99. public string Error
  100. {
  101. get {
  102. const int bufsize = 1024;
  103. byte[] buffer = new byte[bufsize];
  104. int result = SocketError (m_NativeRef, buffer, bufsize);
  105. if (result == 0)
  106. return null;
  107. return Encoding.UTF8.GetString (buffer);
  108. }
  109. }
  110. #else
  111. WebSocketSharp.WebSocket m_Socket;
  112. Queue<byte[]> m_Messages = new Queue<byte[]>();
  113. bool m_IsConnected = false;
  114. string m_Error = null;
  115. public void Connect()
  116. {
  117. m_Socket = new WebSocketSharp.WebSocket(mUrl.ToString(), new string[] {this.protocols});
  118. m_Socket.Log.Output = (ld, f) =>
  119. {
  120. var s = string.Format("WebSocketSharp: {0}", ld.Message);
  121. switch (ld.Level)
  122. {
  123. case WebSocketSharp.LogLevel.Trace:
  124. case WebSocketSharp.LogLevel.Debug:
  125. DebugReturn(DebugLevel.ALL, s);
  126. break;
  127. case WebSocketSharp.LogLevel.Info:
  128. DebugReturn(DebugLevel.INFO, s);
  129. break;
  130. case WebSocketSharp.LogLevel.Warn:
  131. DebugReturn(DebugLevel.WARNING, s);
  132. break;
  133. case WebSocketSharp.LogLevel.Error:
  134. case WebSocketSharp.LogLevel.Fatal:
  135. DebugReturn(DebugLevel.ERROR, s);
  136. break;
  137. }
  138. };
  139. string user = null;
  140. string pass = null;
  141. if (!String.IsNullOrEmpty(mProxyAddress))
  142. {
  143. var authDelim = mProxyAddress.IndexOf("@");
  144. if (authDelim != -1)
  145. {
  146. user = mProxyAddress.Substring(0, authDelim);
  147. mProxyAddress = mProxyAddress.Substring(authDelim + 1);
  148. var passDelim = user.IndexOf(":");
  149. if (passDelim != -1)
  150. {
  151. pass = user.Substring(passDelim + 1);
  152. user = user.Substring(0, passDelim);
  153. }
  154. }
  155. // throws an exception, if scheme not specified
  156. m_Socket.SetProxy("http://" + mProxyAddress, user, pass);
  157. }
  158. if (m_Socket.IsSecure)
  159. {
  160. m_Socket.SslConfiguration.EnabledSslProtocols = m_Socket.SslConfiguration.EnabledSslProtocols | (SslProtocols)(3072 | 768);
  161. }
  162. m_Socket.OnMessage += (sender, e) => { m_Messages.Enqueue(e.RawData); };
  163. m_Socket.OnOpen += (sender, e) => { m_IsConnected = true; };
  164. m_Socket.OnError += (sender, e) => { m_Error = e.Message + (e.Exception == null ? "" : " / " + e.Exception); };
  165. this.m_Socket.OnClose += SocketOnClose;
  166. m_Socket.ConnectAsync();
  167. }
  168. private void SocketOnClose(object sender, CloseEventArgs e)
  169. {
  170. //UnityEngine.Debug.Log(e.Code.ToString());
  171. // this code is used for cases when the socket failed to get created (specifically used to detect "blocked by Windows firewall")
  172. // for some reason this situation is not calling OnError
  173. if (e.Code == 1006)
  174. {
  175. this.m_Error = e.Reason;
  176. this.m_IsConnected = false;
  177. }
  178. }
  179. public bool Connected
  180. {
  181. get { return m_IsConnected; }
  182. }
  183. public void Send(byte[] buffer)
  184. {
  185. m_Socket.Send(buffer);
  186. }
  187. public byte[] Recv()
  188. {
  189. if (m_Messages.Count == 0)
  190. return null;
  191. return m_Messages.Dequeue();
  192. }
  193. public void Close()
  194. {
  195. m_Socket.Close();
  196. }
  197. public string Error
  198. {
  199. get { return m_Error; }
  200. }
  201. #endif
  202. }
  203. }
  204. #endif