SHARE
TWEET

Untitled

a guest Feb 18th, 2020 62 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  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. }
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
Top