Advertisement
Guest User

Untitled

a guest
Feb 18th, 2020
113
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 10.28 KB | None | 0 0
  1. #region License
  2. /*
  3. * SocketIO.cs
  4. *
  5. * The MIT License
  6. *
  7. * Copyright (c) 2014 Fabio Panettieri
  8. *
  9. * Permission is hereby granted, free of charge, to any person obtaining a copy
  10. * of this software and associated documentation files (the "Software"), to deal
  11. * in the Software without restriction, including without limitation the rights
  12. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  13. * copies of the Software, and to permit persons to whom the Software is
  14. * furnished to do so, subject to the following conditions:
  15. *
  16. * The above copyright notice and this permission notice shall be included in
  17. * all copies or substantial portions of the Software.
  18. *
  19. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  20. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  22. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  23. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  24. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  25. * THE SOFTWARE.
  26. */
  27.  
  28. #endregion
  29.  
  30. //#define SOCKET_IO_DEBUG // Uncomment this for debug
  31. using System;
  32. using System.Collections;
  33. using System.Collections.Generic;
  34. using System.Threading;
  35. using UnityEngine;
  36. using WebSocketSharp;
  37. using WebSocketSharp.Net;
  38.  
  39. namespace SocketIO
  40. {
  41. public class SocketIOComponent : MonoBehaviour
  42. {
  43. #region Public Properties
  44.  
  45. [Header("Socket IO Component")]
  46. public string url = "ws://127.0.0.1:4567/socket.io/?EIO=4&transport=websocket";
  47. public bool autoConnect = true;
  48. public int reconnectDelay = 5;
  49. public float ackExpirationTime = 1800f;
  50. public float pingInterval = 25f;
  51. public float pingTimeout = 60f;
  52.  
  53. public WebSocket socket { get { return ws; } }
  54. public string sid { get; set; }
  55. public bool IsConnected { get { return connected; } }
  56.  
  57. #endregion
  58.  
  59. #region Private Properties
  60.  
  61. private volatile bool connected;
  62. private volatile bool thPinging;
  63. private volatile bool thPong;
  64. private volatile bool wsConnected;
  65.  
  66. private Thread socketThread;
  67. private Thread pingThread;
  68. private WebSocket ws;
  69.  
  70. private Encoder encoder;
  71. private Decoder decoder;
  72. private Parser parser;
  73.  
  74. private Dictionary<string, List<Action<SocketIOEvent>>> handlers;
  75. private List<Ack> ackList;
  76.  
  77. private int packetId;
  78.  
  79. private object eventQueueLock;
  80. private Queue<SocketIOEvent> eventQueue;
  81.  
  82. private object ackQueueLock;
  83. private Queue<Packet> ackQueue;
  84.  
  85. #endregion
  86.  
  87. #if SOCKET_IO_DEBUG
  88. public Action<string> debugMethod;
  89. #endif
  90.  
  91. #region Unity interface
  92.  
  93. public void Awake()
  94. {
  95. encoder = new Encoder();
  96. decoder = new Decoder();
  97. parser = new Parser();
  98. handlers = new Dictionary<string, List<Action<SocketIOEvent>>>();
  99. ackList = new List<Ack>();
  100. sid = null;
  101. packetId = 0;
  102.  
  103. ws = new WebSocket(url);
  104. ws.OnOpen += OnOpen;
  105. ws.OnMessage += OnMessage;
  106. ws.OnError += OnError;
  107. ws.OnClose += OnClose;
  108. wsConnected = false;
  109.  
  110. eventQueueLock = new object();
  111. eventQueue = new Queue<SocketIOEvent>();
  112.  
  113. ackQueueLock = new object();
  114. ackQueue = new Queue<Packet>();
  115.  
  116. connected = false;
  117.  
  118. #if SOCKET_IO_DEBUG
  119. if(debugMethod == null) { debugMethod = Debug.Log; };
  120. #endif
  121. }
  122.  
  123. public virtual void Start()
  124. {
  125. if (autoConnect) { Connect(); }
  126. }
  127.  
  128. public virtual void Update()
  129. {
  130. lock(eventQueueLock){
  131. while(eventQueue.Count > 0){
  132. EmitEvent(eventQueue.Dequeue());
  133. }
  134. }
  135.  
  136. lock(ackQueueLock){
  137. while(ackQueue.Count > 0){
  138. InvokeAck(ackQueue.Dequeue());
  139. }
  140. }
  141.  
  142. if(wsConnected != ws.IsConnected){
  143. wsConnected = ws.IsConnected;
  144. if(wsConnected){
  145. EmitEvent("connect");
  146. } else {
  147. EmitEvent("disconnect");
  148. }
  149. }
  150.  
  151. // GC expired acks
  152. if(ackList.Count == 0) { return; }
  153. if(DateTime.Now.Subtract(ackList[0].time).TotalSeconds < ackExpirationTime){ return; }
  154. ackList.RemoveAt(0);
  155. }
  156.  
  157. public void OnDestroy()
  158. {
  159. if (socketThread != null) { socketThread.Abort(); }
  160. if (pingThread != null) { pingThread.Abort(); }
  161. }
  162.  
  163. public void OnApplicationQuit()
  164. {
  165. Close();
  166. }
  167.  
  168. #endregion
  169.  
  170. #region Public Interface
  171.  
  172. public void Connect()
  173. {
  174. connected = true;
  175.  
  176. socketThread = new Thread(RunSocketThread);
  177. socketThread.Start(ws);
  178.  
  179. pingThread = new Thread(RunPingThread);
  180. pingThread.Start(ws);
  181. }
  182.  
  183. public void Close()
  184. {
  185. EmitClose();
  186. connected = false;
  187. }
  188.  
  189. public void On(string ev, Action<SocketIOEvent> callback)
  190. {
  191. if (!handlers.ContainsKey(ev)) {
  192. handlers[ev] = new List<Action<SocketIOEvent>>();
  193. }
  194. handlers[ev].Add(callback);
  195. }
  196.  
  197. public void Off(string ev, Action<SocketIOEvent> callback)
  198. {
  199. if (!handlers.ContainsKey(ev)) {
  200. #if SOCKET_IO_DEBUG
  201. debugMethod.Invoke("[SocketIO] No callbacks registered for event: " + ev);
  202. #endif
  203. return;
  204. }
  205.  
  206. List<Action<SocketIOEvent>> l = handlers [ev];
  207. if (!l.Contains(callback)) {
  208. #if SOCKET_IO_DEBUG
  209. debugMethod.Invoke("[SocketIO] Couldn't remove callback action for event: " + ev);
  210. #endif
  211. return;
  212. }
  213.  
  214. l.Remove(callback);
  215. if (l.Count == 0) {
  216. handlers.Remove(ev);
  217. }
  218. }
  219.  
  220. public void Emit(string ev)
  221. {
  222. EmitMessage(-1, string.Format("[\"{0}\"]", ev));
  223. }
  224.  
  225. public void Emit(string ev, Action<JSONObject> action)
  226. {
  227. EmitMessage(++packetId, string.Format("[\"{0}\"]", ev));
  228. ackList.Add(new Ack(packetId, action));
  229. }
  230.  
  231. public void Emit(string ev, JSONObject data)
  232. {
  233. EmitMessage(-1, string.Format("[\"{0}\",{1}]", ev, data));
  234. }
  235.  
  236. public void Emit(string ev, JSONObject data, Action<JSONObject> action)
  237. {
  238. EmitMessage(++packetId, string.Format("[\"{0}\",{1}]", ev, data));
  239. ackList.Add(new Ack(packetId, action));
  240. }
  241.  
  242. #endregion
  243.  
  244. #region Private Methods
  245.  
  246. private void RunSocketThread(object obj)
  247. {
  248. WebSocket webSocket = (WebSocket)obj;
  249. while(connected){
  250. if(webSocket.IsConnected){
  251. Thread.Sleep(reconnectDelay);
  252. } else {
  253. webSocket.Connect();
  254. }
  255. }
  256. webSocket.Close();
  257. }
  258.  
  259. private void RunPingThread(object obj)
  260. {
  261. WebSocket webSocket = (WebSocket)obj;
  262.  
  263. int timeoutMilis = Mathf.FloorToInt(pingTimeout * 1000);
  264. int intervalMilis = Mathf.FloorToInt(pingInterval * 1000);
  265.  
  266. DateTime pingStart;
  267.  
  268. while(connected)
  269. {
  270. if(!wsConnected){
  271. Thread.Sleep(reconnectDelay);
  272. } else {
  273. thPinging = true;
  274. thPong = false;
  275.  
  276. EmitPacket(new Packet(EnginePacketType.PING));
  277. pingStart = DateTime.Now;
  278.  
  279. while(webSocket.IsConnected && thPinging && (DateTime.Now.Subtract(pingStart).TotalSeconds < timeoutMilis)){
  280. Thread.Sleep(200);
  281. }
  282.  
  283. if(!thPong){
  284. webSocket.Close();
  285. }
  286.  
  287. Thread.Sleep(intervalMilis);
  288. }
  289. }
  290. }
  291.  
  292. private void EmitMessage(int id, string raw)
  293. {
  294. EmitPacket(new Packet(EnginePacketType.MESSAGE, SocketPacketType.EVENT, 0, "/", id, new JSONObject(raw)));
  295. }
  296.  
  297. private void EmitClose()
  298. {
  299. EmitPacket(new Packet(EnginePacketType.MESSAGE, SocketPacketType.DISCONNECT, 0, "/", -1, new JSONObject("")));
  300. EmitPacket(new Packet(EnginePacketType.CLOSE));
  301. }
  302.  
  303. private void EmitPacket(Packet packet)
  304. {
  305. #if SOCKET_IO_DEBUG
  306. debugMethod.Invoke("[SocketIO] " + packet);
  307. #endif
  308.  
  309. #pragma warning disable 0168
  310. try {
  311. ws.Send(encoder.Encode(packet));
  312. } catch(SocketIOException ex) {
  313. #if SOCKET_IO_DEBUG
  314. debugMethod.Invoke(ex.ToString());
  315. #endif
  316. }
  317. #pragma warning restore 0168
  318. }
  319.  
  320. private void OnOpen(object sender, EventArgs e)
  321. {
  322. EmitEvent("open");
  323. }
  324.  
  325. private void OnMessage(object sender, MessageEventArgs e)
  326. {
  327. #if SOCKET_IO_DEBUG
  328. debugMethod.Invoke("[SocketIO] Raw message: " + e.Data);
  329. #endif
  330. Packet packet = decoder.Decode(e);
  331.  
  332. switch (packet.enginePacketType) {
  333. case EnginePacketType.OPEN: HandleOpen(packet); break;
  334. case EnginePacketType.CLOSE: EmitEvent("close"); break;
  335. case EnginePacketType.PING: HandlePing(); break;
  336. case EnginePacketType.PONG: HandlePong(); break;
  337. case EnginePacketType.MESSAGE: HandleMessage(packet); break;
  338. }
  339. }
  340.  
  341. private void HandleOpen(Packet packet)
  342. {
  343. #if SOCKET_IO_DEBUG
  344. debugMethod.Invoke("[SocketIO] Socket.IO sid: " + packet.json["sid"].str);
  345. #endif
  346. sid = packet.json["sid"].str;
  347. EmitEvent("open");
  348. }
  349.  
  350. private void HandlePing()
  351. {
  352. EmitPacket(new Packet(EnginePacketType.PONG));
  353. }
  354.  
  355. private void HandlePong()
  356. {
  357. thPong = true;
  358. thPinging = false;
  359. }
  360.  
  361. private void HandleMessage(Packet packet)
  362. {
  363. if(packet.json == null) { return; }
  364.  
  365. if(packet.socketPacketType == SocketPacketType.ACK){
  366. for(int i = 0; i < ackList.Count; i++){
  367. if(ackList[i].packetId != packet.id){ continue; }
  368. lock(ackQueueLock){ ackQueue.Enqueue(packet); }
  369. return;
  370. }
  371.  
  372. #if SOCKET_IO_DEBUG
  373. debugMethod.Invoke("[SocketIO] Ack received for invalid Action: " + packet.id);
  374. #endif
  375. }
  376.  
  377. if (packet.socketPacketType == SocketPacketType.EVENT) {
  378. SocketIOEvent e = parser.Parse(packet.json);
  379. lock(eventQueueLock){ eventQueue.Enqueue(e); }
  380. }
  381. }
  382.  
  383. private void OnError(object sender, ErrorEventArgs e)
  384. {
  385. EmitEvent("error");
  386. }
  387.  
  388. private void OnClose(object sender, CloseEventArgs e)
  389. {
  390. EmitEvent("close");
  391. }
  392.  
  393. private void EmitEvent(string type)
  394. {
  395. EmitEvent(new SocketIOEvent(type));
  396. }
  397.  
  398. private void EmitEvent(SocketIOEvent ev)
  399. {
  400. if (!handlers.ContainsKey(ev.name)) { return; }
  401. foreach (Action<SocketIOEvent> handler in this.handlers[ev.name]) {
  402. #pragma warning disable 0168
  403. try{
  404. handler(ev);
  405. } catch(Exception ex){
  406. #if SOCKET_IO_DEBUG
  407. debugMethod.Invoke(ex.ToString());
  408. #endif
  409. }
  410. #pragma warning restore 0168
  411. }
  412. }
  413.  
  414. private void InvokeAck(Packet packet)
  415. {
  416. Ack ack;
  417. for(int i = 0; i < ackList.Count; i++){
  418. if(ackList[i].packetId != packet.id){ continue; }
  419. ack = ackList[i];
  420. ackList.RemoveAt(i);
  421. ack.Invoke(packet.json);
  422. return;
  423. }
  424. }
  425.  
  426. #endregion
  427. }
  428. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement