WebSocket.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. #if UNITY_WEBGL // && !UNITY_EDITOR
  2. using System;
  3. namespace XGame.Framework.Network.Web
  4. {
  5. public class WebSocket : IRemoteSession, IDisposable
  6. {
  7. internal WebSocketState ReadyState => (WebSocketState)WebSocketService.WebSocketGetState(instanceId);
  8. private volatile SessionStatus _status = SessionStatus.FREE;
  9. public SessionStatus Status => _status;
  10. public SessionAddress Address { get; private set; }
  11. internal int instanceId = 0;
  12. private ISessionContext _context;
  13. internal WebSocket(ISessionContext context)
  14. {
  15. _context = context;
  16. }
  17. //public WebSocket(string address)
  18. //{
  19. // this.Address = address;
  20. // AllocateInstance();
  21. //}
  22. //public WebSocket(string address, string subProtocol)
  23. //{
  24. // this.Address = address;
  25. // this.SubProtocols = new string[] { subProtocol };
  26. // AllocateInstance();
  27. //}
  28. //public WebSocket(string address, string[] subProtocols)
  29. //{
  30. // this.Address = address;
  31. // this.SubProtocols = subProtocols;
  32. // AllocateInstance();
  33. //}
  34. //internal void AllocateInstance()
  35. //{
  36. // instanceId = WebSocketService.AllocateInstance(this.Address);
  37. // Log($"Allocate socket with instanceId: {instanceId}");
  38. // if (this.SubProtocols == null) return;
  39. // foreach (var protocol in this.SubProtocols)
  40. // {
  41. // if (string.IsNullOrEmpty(protocol)) continue;
  42. // Log($"Add Sub Protocol {protocol}, with instanceId: {instanceId}");
  43. // int code = WebSocketService.WebSocketAddSubProtocol(instanceId, protocol);
  44. // if (code < 0)
  45. // {
  46. // HandleOnError(GetErrorMessageFromCode(code));
  47. // break;
  48. // }
  49. // }
  50. //}
  51. //~WebSocket()
  52. //{
  53. // Log($"Free socket with instanceId: {instanceId}");
  54. // WebSocketService.WebSocketFree(instanceId);
  55. //}
  56. //public void ConnectAsync()
  57. //{
  58. // Log($"Connect with instanceId: {instanceId}");
  59. // WebSocketService.Add(this);
  60. // int code = WebSocketService.WebSocketConnect(instanceId);
  61. // if (code < 0) HandleOnError(GetErrorMessageFromCode(code));
  62. //}
  63. //public void CloseAsync()
  64. //{
  65. // Log($"Close with instanceId: {instanceId}");
  66. // int code = WebSocketService.WebSocketClose(instanceId, (int)CloseStatusCode.Normal, "Normal Closure");
  67. // if (code < 0) HandleOnError(GetErrorMessageFromCode(code));
  68. //}
  69. //public void SendAsync(string text)
  70. //{
  71. // Log($"Send, type: {Opcode.Text}, size: {text.Length}");
  72. // int code = WebSocketService.WebSocketSendStr(instanceId, text);
  73. // if (code < 0) HandleOnError(GetErrorMessageFromCode(code));
  74. //}
  75. //public void SendAsync(byte[] data)
  76. //{
  77. // Log($"Send, type: {Opcode.Binary}, size: {data.Length}");
  78. // int code = WebSocketService.WebSocketSend(instanceId, data, data.Length);
  79. // if (code < 0) HandleOnError(GetErrorMessageFromCode(code));
  80. //}
  81. internal void HandleOnOpen()
  82. {
  83. Log.Debug($"[WebSocket] OnOpen.");
  84. _status = SessionStatus.CONNECTED;
  85. _context.Synchronizer.Enqueue(ESessionCode.Connected, null);
  86. }
  87. internal void HandleOnMessage(byte[] rawData, int offset, int length)
  88. {
  89. Log.Debug($"[WebSocket] OnMessage.");
  90. _context.Synchronizer.Enqueue(rawData, offset, length);
  91. }
  92. internal void HandleOnMessageStr(string data)
  93. {
  94. Log.Error($"[WebSocket] OnMessageStr. data:{data}");
  95. }
  96. internal void HandleOnClose(ushort code, string reason)
  97. {
  98. Log.Debug($"[WebSocket] OnClose, code: {code}, reason: {reason}");
  99. if (code != (int)CloseStatusCode.Normal)
  100. OnDisconnectError(new WebSocketException(reason, code));
  101. OnDisconnect();
  102. }
  103. internal void HandleOnError(string msg)
  104. {
  105. Log.Debug("[WebSocket] OnError, error: " + msg);
  106. OnReceiveError(new WebSocketException(msg, 0));
  107. }
  108. internal static Exception GetErrorMessageFromCode(int errorCode)
  109. {
  110. string message;
  111. switch (errorCode)
  112. {
  113. case -1:
  114. message = "WebSocket instance not found.";
  115. break;
  116. case -2:
  117. message = "WebSocket is already connected or in connecting state.";
  118. break;
  119. case -3:
  120. message = "WebSocket is not connected.";
  121. break;
  122. case -4:
  123. message = "WebSocket is already closing.";
  124. break;
  125. case -5:
  126. message = "WebSocket is already closed.";
  127. break;
  128. case -6:
  129. message = "WebSocket is not in open state.";
  130. break;
  131. case -7:
  132. message = "Cannot close WebSocket, An invalid code was specified or reason is too long.";
  133. break;
  134. case -8:
  135. message = "Not support buffer slice. ";
  136. break;
  137. default:
  138. message = $"Unknown error code {errorCode}.";
  139. break;
  140. }
  141. return new WebSocketException(message, errorCode);
  142. }
  143. //[System.Diagnostics.Conditional("UNITY_WEB_SOCKET_LOG")]
  144. //static void Log(string msg)
  145. //{
  146. // var time = DateTime.Now.ToString("HH:mm:ss.fff");
  147. // UnityEngine.Debug.Log($"[{time}][UnityWebSocket] {msg}");
  148. //}
  149. public void Connect(AddressInfo info)
  150. {
  151. if (Status != SessionStatus.FREE)
  152. {
  153. Log.Warn($"[WebSocket] SessionStatus:{Status} 已连接或者正在连接,又尝试连接");
  154. return;
  155. }
  156. OnStartConnect();
  157. if (Address == null ||
  158. Address.Domain != info.Address ||
  159. Address.PORT == info.Port ||
  160. Address.ProtocolType == info.ProtocolType)
  161. {
  162. Address = new SessionAddress(info);
  163. }
  164. var uri = GetUri(Address);
  165. if (string.IsNullOrEmpty(uri))
  166. {
  167. OnConnectFail(new Exception($"[WebSocket] ProtocolType {Address.ProtocolType} is not find"));
  168. return;
  169. }
  170. instanceId = WebSocketService.AllocateInstance(uri);
  171. WebSocketService.Add(this);
  172. int code = WebSocketService.WebSocketConnect(instanceId);
  173. if (code < 0)
  174. OnConnectFail(GetErrorMessageFromCode(code));
  175. }
  176. public bool IsConnected(bool bPrecice)
  177. {
  178. return Status == SessionStatus.CONNECTED && ReadyState == WebSocketState.Open;
  179. }
  180. public void Disconnect()
  181. {
  182. if (Status == SessionStatus.FREE)
  183. return;
  184. _status = SessionStatus.FREE;
  185. int code = WebSocketService.WebSocketClose(instanceId, (int)CloseStatusCode.Normal, "Normal Closure");
  186. if (code < 0)
  187. OnDisconnectError(GetErrorMessageFromCode(code));
  188. //else
  189. // OnDisconnect();
  190. }
  191. public bool Send(byte[] bytes, int offset, int length)
  192. {
  193. // offset 固定为零
  194. int code = WebSocketService.WebSocketSend(instanceId, bytes, length);
  195. if (code < 0)
  196. OnSendError(GetErrorMessageFromCode(code));
  197. SessionBufferPool.Recycle(bytes);
  198. return code >= 0;
  199. }
  200. void IDisposable.Dispose()
  201. {
  202. Disconnect();
  203. _context = null;
  204. WebSocketService.WebSocketFree(instanceId);
  205. }
  206. string GetUri(SessionAddress info)
  207. {
  208. string url;
  209. switch (info.ProtocolType)
  210. {
  211. case ProtocolType.WS:
  212. if (string.IsNullOrEmpty(info.URI))
  213. url = $"ws://{info.Domain}:{info.PORT}";
  214. else
  215. url = info.URI;
  216. break;
  217. case ProtocolType.WSS:
  218. if (string.IsNullOrEmpty(info.URI))
  219. url = $"wss://{info.Domain}:{info.PORT}";
  220. else
  221. url = info.URI;
  222. break;
  223. default:
  224. return url = string.Empty;
  225. }
  226. return url;
  227. }
  228. #region Event
  229. private void OnConnectFail(Exception e)
  230. {
  231. _status = SessionStatus.CONNECT_FAIL;
  232. _context.Synchronizer.Enqueue(ESessionCode.ConnectFail, e);
  233. }
  234. //private void OnConnectTimeout()
  235. //{
  236. // _status = SessionStatus.CONNECT_TIMEOUT;
  237. // _context.Synchronizer.Enqueue(ESessionCode.ConnectTimeout, null);
  238. //}
  239. //private void OnConnected()
  240. //{
  241. // _status = SessionStatus.CONNECTED;
  242. // _context.Synchronizer.Enqueue(ESessionCode.Connected, null);
  243. //}
  244. private void OnStartConnect()
  245. {
  246. _context.Synchronizer.Enqueue(ESessionCode.StartConnect, null);
  247. }
  248. private void OnDisconnect()
  249. {
  250. _context.Synchronizer.Enqueue(ESessionCode.Disconnect, null);
  251. }
  252. private void OnDisconnectError(Exception e)
  253. {
  254. _context.Synchronizer.Enqueue(ESessionCode.DisconnectError, e);
  255. }
  256. private void OnSendError(Exception e)
  257. {
  258. _status = SessionStatus.SEND_ERROR;
  259. _context.Synchronizer.Enqueue(ESessionCode.SendException, e);
  260. }
  261. private void OnReceiveError(Exception e)
  262. {
  263. _status = SessionStatus.RECV_ERROR;
  264. _context.Synchronizer.Enqueue(ESessionCode.RecvError, e);
  265. }
  266. //private void OnRemoteClose()
  267. //{
  268. // _status = SessionStatus.CLOSED;
  269. // _context.Synchronizer.Enqueue(ESessionCode.SessionClose, null);
  270. //}
  271. #endregion
  272. }
  273. }
  274. #endif