SocketManager.cs 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749
  1. #if !BESTHTTP_DISABLE_SOCKETIO
  2. using System;
  3. using System.Collections.Generic;
  4. using BestHTTP.SocketIO.Transports;
  5. using BestHTTP.Extensions;
  6. using BestHTTP.SocketIO.JsonEncoders;
  7. using BestHTTP.SocketIO.Events;
  8. namespace BestHTTP.SocketIO
  9. {
  10. public sealed class SocketManager : IHeartbeat, IManager
  11. {
  12. /// <summary>
  13. /// Possible states of a SocketManager instance.
  14. /// </summary>
  15. public enum States
  16. {
  17. /// <summary>
  18. /// Initial state of the SocketManager
  19. /// </summary>
  20. Initial,
  21. /// <summary>
  22. /// The SocketManager is currently opening.
  23. /// </summary>
  24. Opening,
  25. /// <summary>
  26. /// The SocketManager is open, events can be sent to the server.
  27. /// </summary>
  28. Open,
  29. /// <summary>
  30. /// Paused for transport upgrade
  31. /// </summary>
  32. Paused,
  33. /// <summary>
  34. /// An error occurred, the SocketManager now trying to connect again to the server.
  35. /// </summary>
  36. Reconnecting,
  37. /// <summary>
  38. /// The SocketManager is closed, initiated by the user or by the server
  39. /// </summary>
  40. Closed
  41. }
  42. /// <summary>
  43. /// The default Json encode/decoder that will be used to encode/decode the event arguments.
  44. /// </summary>
  45. public static IJsonEncoder DefaultEncoder = new DefaultJSonEncoder();
  46. /// <summary>
  47. /// Supported Socket.IO protocol version
  48. /// </summary>
  49. public int ProtocolVersion { get { return this.Options.ServerVersion == SupportedSocketIOVersions.v3 ? 4 : 3; } }
  50. #region Public Properties
  51. /// <summary>
  52. /// The current state of this Socket.IO manager.
  53. /// </summary>
  54. public States State { get { return state; } private set { PreviousState = state; state = value; } }
  55. private States state;
  56. /// <summary>
  57. /// The SocketOptions instance that this manager will use.
  58. /// </summary>
  59. public SocketOptions Options { get; private set; }
  60. /// <summary>
  61. /// The Uri to the Socket.IO endpoint.
  62. /// </summary>
  63. public Uri Uri { get; private set; }
  64. /// <summary>
  65. /// The server sent and parsed Handshake data.
  66. /// </summary>
  67. public HandshakeData Handshake { get; private set; }
  68. /// <summary>
  69. /// The currently used main transport instance.
  70. /// </summary>
  71. public ITransport Transport { get; private set; }
  72. /// <summary>
  73. /// The Request counter for request-based transports.
  74. /// </summary>
  75. public ulong RequestCounter { get; internal set; }
  76. /// <summary>
  77. /// The root("/") Socket.
  78. /// </summary>
  79. public Socket Socket { get { return GetSocket(); } }
  80. /// <summary>
  81. /// Indexer to access socket associated to the given namespace.
  82. /// </summary>
  83. public Socket this[string nsp] { get { return GetSocket(nsp); } }
  84. /// <summary>
  85. /// How many reconnect attempts made.
  86. /// </summary>
  87. public int ReconnectAttempts { get; private set; }
  88. /// <summary>
  89. /// The JSon encoder that will be used to encode the sent data to json and decode the received json to an object list.
  90. /// </summary>
  91. public IJsonEncoder Encoder { get; set; }
  92. #endregion
  93. #region Internal Properties
  94. /// <summary>
  95. /// Timestamp support to the request based transports.
  96. /// </summary>
  97. internal UInt64 Timestamp { get { return (UInt64)(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalMilliseconds; } }
  98. /// <summary>
  99. /// Auto-incrementing property to return Ack ids.
  100. /// </summary>
  101. internal int NextAckId { get { return System.Threading.Interlocked.Increment(ref nextAckId); } }
  102. private int nextAckId;
  103. /// <summary>
  104. /// Internal property to store the previous state of the manager.
  105. /// </summary>
  106. internal States PreviousState { get; private set; }
  107. /// <summary>
  108. /// Transport currently upgrading.
  109. /// </summary>
  110. internal ITransport UpgradingTransport { get; set; }
  111. #endregion
  112. #region Privates
  113. /// <summary>
  114. /// Namespace name -> Socket mapping
  115. /// </summary>
  116. private Dictionary<string, Socket> Namespaces = new Dictionary<string, Socket>();
  117. /// <summary>
  118. /// List of the sockets to able to iterate over them easily.
  119. /// </summary>
  120. private List<Socket> Sockets = new List<Socket>();
  121. /// <summary>
  122. /// List of unsent packets. Only instantiated when we have to use it.
  123. /// </summary>
  124. private List<Packet> OfflinePackets;
  125. /// <summary>
  126. /// When we sent out the last heartbeat(Ping) message.
  127. /// </summary>
  128. private DateTime LastHeartbeat = DateTime.MinValue;
  129. /// <summary>
  130. /// When we have to try to do a reconnect attempt
  131. /// </summary>
  132. private DateTime ReconnectAt;
  133. /// <summary>
  134. /// When we started to connect to the server.
  135. /// </summary>
  136. private DateTime ConnectionStarted;
  137. /// <summary>
  138. /// Private flag to avoid multiple Close call
  139. /// </summary>
  140. private bool closing;
  141. /// <summary>
  142. /// Whether the connection is waiting for a ping response.
  143. /// </summary>
  144. private bool IsWaitingPong;
  145. /// <summary>
  146. /// In Engine.io v4 / socket.io v3 the server sends the ping messages, not the client.
  147. /// </summary>
  148. private DateTime lastPingReceived;
  149. #endregion
  150. #region Constructors
  151. /// <summary>
  152. /// Constructor to create a SocketManager instance that will connect to the given uri.
  153. /// </summary>
  154. public SocketManager(Uri uri)
  155. :this(uri, new SocketOptions())
  156. {
  157. }
  158. /// <summary>
  159. /// Constructor to create a SocketManager instance.
  160. /// </summary>
  161. public SocketManager(Uri uri, SocketOptions options)
  162. {
  163. Uri = uri;
  164. Options = options ?? new SocketOptions();
  165. State = States.Initial;
  166. PreviousState = States.Initial;
  167. Encoder = SocketManager.DefaultEncoder;
  168. }
  169. #endregion
  170. /// <summary>
  171. /// Returns with the "/" namespace, the same as the Socket property.
  172. /// </summary>
  173. public Socket GetSocket()
  174. {
  175. return GetSocket("/");
  176. }
  177. /// <summary>
  178. /// Returns with the specified namespace
  179. /// </summary>
  180. public Socket GetSocket(string nsp)
  181. {
  182. if (string.IsNullOrEmpty(nsp))
  183. throw new ArgumentNullException("Namespace parameter is null or empty!");
  184. /*if (nsp[0] != '/')
  185. nsp = "/" + nsp;*/
  186. Socket socket = null;
  187. if (!Namespaces.TryGetValue(nsp, out socket))
  188. {
  189. // No socket found, create one
  190. socket = new Socket(nsp, this);
  191. Namespaces.Add(nsp, socket);
  192. Sockets.Add(socket);
  193. (socket as ISocket).Open();
  194. }
  195. return socket;
  196. }
  197. /// <summary>
  198. /// Internal function to remove a Socket instance from this manager.
  199. /// </summary>
  200. /// <param name="socket"></param>
  201. void IManager.Remove(Socket socket)
  202. {
  203. Namespaces.Remove(socket.Namespace);
  204. Sockets.Remove(socket);
  205. if (Sockets.Count == 0)
  206. Close();
  207. }
  208. #region Connection to the server, and upgrading
  209. /// <summary>
  210. /// This function will begin to open the Socket.IO connection by sending out the handshake request.
  211. /// If the Options' AutoConnect is true, it will be called automatically.
  212. /// </summary>
  213. public void Open()
  214. {
  215. if (State != States.Initial &&
  216. State != States.Closed &&
  217. State != States.Reconnecting)
  218. return;
  219. HTTPManager.Logger.Information("SocketManager", "Opening");
  220. ReconnectAt = DateTime.MinValue;
  221. switch (Options.ConnectWith)
  222. {
  223. case TransportTypes.Polling: Transport = new PollingTransport(this); break;
  224. #if !BESTHTTP_DISABLE_WEBSOCKET
  225. case TransportTypes.WebSocket:
  226. Transport = new WebSocketTransport(this);
  227. break;
  228. #endif
  229. }
  230. Transport.Open();
  231. (this as IManager).EmitEvent("connecting");
  232. State = States.Opening;
  233. ConnectionStarted = DateTime.UtcNow;
  234. HTTPManager.Heartbeats.Subscribe(this);
  235. // The root namespace will be opened by default
  236. GetSocket("/");
  237. }
  238. /// <summary>
  239. /// Closes this Socket.IO connection.
  240. /// </summary>
  241. public void Close()
  242. {
  243. (this as IManager).Close(true);
  244. }
  245. /// <summary>
  246. /// Closes this Socket.IO connection.
  247. /// </summary>
  248. void IManager.Close(bool removeSockets)
  249. {
  250. if (State == States.Closed || closing)
  251. return;
  252. closing = true;
  253. HTTPManager.Logger.Information("SocketManager", "Closing");
  254. HTTPManager.Heartbeats.Unsubscribe(this);
  255. // Disconnect the sockets. The Disconnect function will call the Remove function to remove it from the Sockets list.
  256. if (removeSockets)
  257. while (Sockets.Count > 0)
  258. (Sockets[Sockets.Count - 1] as ISocket).Disconnect(removeSockets);
  259. else
  260. for (int i = 0; i < Sockets.Count; ++i)
  261. (Sockets[i] as ISocket).Disconnect(removeSockets);
  262. // Set to Closed after Socket's Disconnect. This way we can send the disconnect events to the server.
  263. State = States.Closed;
  264. LastHeartbeat = DateTime.MinValue;
  265. IsWaitingPong = false;
  266. lastPingReceived = DateTime.MinValue;
  267. if (removeSockets && OfflinePackets != null)
  268. OfflinePackets.Clear();
  269. // Remove the references from the dictionary too.
  270. if (removeSockets)
  271. Namespaces.Clear();
  272. Handshake = null;
  273. if (Transport != null)
  274. Transport.Close();
  275. Transport = null;
  276. if (UpgradingTransport != null)
  277. UpgradingTransport.Close();
  278. UpgradingTransport = null;
  279. closing = false;
  280. }
  281. /// <summary>
  282. /// Called from a ITransport implementation when an error occurs and we may have to try to reconnect.
  283. /// </summary>
  284. void IManager.TryToReconnect()
  285. {
  286. if (State == States.Reconnecting ||
  287. State == States.Closed)
  288. return;
  289. if (!Options.Reconnection || HTTPManager.IsQuitting)
  290. {
  291. Close();
  292. return;
  293. }
  294. if (++ReconnectAttempts >= Options.ReconnectionAttempts)
  295. {
  296. (this as IManager).EmitEvent("reconnect_failed");
  297. Close();
  298. return;
  299. }
  300. Random rand = new Random();
  301. int delay = (int)Options.ReconnectionDelay.TotalMilliseconds * ReconnectAttempts;
  302. ReconnectAt = DateTime.UtcNow +
  303. TimeSpan.FromMilliseconds(Math.Min(rand.Next(/*rand min:*/(int)(delay - (delay * Options.RandomizationFactor)),
  304. /*rand max:*/(int)(delay + (delay * Options.RandomizationFactor))),
  305. (int)Options.ReconnectionDelayMax.TotalMilliseconds));
  306. (this as IManager).Close(false);
  307. State = States.Reconnecting;
  308. for (int i = 0; i < Sockets.Count; ++i)
  309. (Sockets[i] as ISocket).Open();
  310. // In the Close() function we unregistered
  311. HTTPManager.Heartbeats.Subscribe(this);
  312. HTTPManager.Logger.Information("SocketManager", "Reconnecting");
  313. }
  314. /// <summary>
  315. /// Called by transports when they are connected to the server.
  316. /// </summary>
  317. bool IManager.OnTransportConnected(ITransport trans)
  318. {
  319. HTTPManager.Logger.Information("SocketManager", string.Format("OnTransportConnected State: {0}, PreviousState: {1}, Current Transport: {2}, Upgrading Transport: {3}", this.State, this.PreviousState, trans.Type, UpgradingTransport != null ? UpgradingTransport.Type.ToString() : "null"));
  320. if (State != States.Opening)
  321. return false;
  322. if (PreviousState == States.Reconnecting)
  323. (this as IManager).EmitEvent("reconnect");
  324. State = States.Open;
  325. if (PreviousState == States.Reconnecting)
  326. (this as IManager).EmitEvent("reconnect_before_offline_packets");
  327. for (int i = 0; i < Sockets.Count; ++i)
  328. {
  329. var socket = Sockets[i];
  330. if (socket != null)
  331. socket.OnTransportOpen();
  332. }
  333. ReconnectAttempts = 0;
  334. // Send out packets that we collected while there were no available transport.
  335. SendOfflinePackets();
  336. #if !BESTHTTP_DISABLE_WEBSOCKET
  337. // Can we upgrade to WebSocket transport?
  338. if (Transport.Type != TransportTypes.WebSocket &&
  339. Handshake.Upgrades.Contains("websocket"))
  340. {
  341. UpgradingTransport = new WebSocketTransport(this);
  342. UpgradingTransport.Open();
  343. }
  344. #endif
  345. return true;
  346. }
  347. void IManager.OnTransportError(ITransport trans, string err)
  348. {
  349. (this as IManager).EmitError(SocketIOErrors.Internal, err);
  350. trans.Close();
  351. (this as IManager).TryToReconnect();
  352. }
  353. void IManager.OnTransportProbed(ITransport trans)
  354. {
  355. HTTPManager.Logger.Information("SocketManager", "\"probe\" packet received");
  356. // If we have to reconnect, we will go straight with the transport we were able to upgrade
  357. Options.ConnectWith = trans.Type;
  358. // Pause ourself to wait for any send and receive turn to finish.
  359. State = States.Paused;
  360. }
  361. #endregion
  362. #region Packet Handling
  363. /// <summary>
  364. /// Select the best transport to send out packets.
  365. /// </summary>
  366. private ITransport SelectTransport()
  367. {
  368. if (State != States.Open || Transport == null)
  369. return null;
  370. return Transport.IsRequestInProgress ? null : Transport;
  371. }
  372. /// <summary>
  373. /// Will select the best transport and sends out all packets that are in the OfflinePackets list.
  374. /// </summary>
  375. private void SendOfflinePackets()
  376. {
  377. ITransport trans = SelectTransport();
  378. // Send out packets that we not sent while no transport was available.
  379. // This function is called before the event handlers get the 'connected' event, so
  380. // theoretically the packet orders are remains.
  381. if (OfflinePackets != null && OfflinePackets.Count > 0 && trans != null)
  382. {
  383. trans.Send(OfflinePackets);
  384. OfflinePackets.Clear();
  385. }
  386. }
  387. /// <summary>
  388. /// Internal function that called from the Socket class. It will send out the packet instantly, or if no transport is available it will store
  389. /// the packet in the OfflinePackets list.
  390. /// </summary>
  391. void IManager.SendPacket(Packet packet)
  392. {
  393. HTTPManager.Logger.Information("SocketManager", "SendPacket " + packet.ToString());
  394. ITransport trans = SelectTransport();
  395. if (trans != null)
  396. {
  397. try
  398. {
  399. trans.Send(packet);
  400. }
  401. catch(Exception ex)
  402. {
  403. (this as IManager).EmitError(SocketIOErrors.Internal, ex.Message + " " + ex.StackTrace);
  404. }
  405. }
  406. else
  407. {
  408. HTTPManager.Logger.Information("SocketManager", "SendPacket - Offline stashing packet");
  409. if (OfflinePackets == null)
  410. OfflinePackets = new List<Packet>();
  411. // The same packet can be sent through multiple Sockets.
  412. OfflinePackets.Add(packet.Clone());
  413. }
  414. }
  415. /// <summary>
  416. /// Called from the currently operating Transport. Will pass forward to the Socket that has to call the callbacks.
  417. /// </summary>
  418. void IManager.OnPacket(Packet packet)
  419. {
  420. if (State == States.Closed)
  421. {
  422. HTTPManager.Logger.Information("SocketManager", "OnPacket - State == States.Closed");
  423. return;
  424. }
  425. switch(packet.TransportEvent)
  426. {
  427. case TransportEventTypes.Open:
  428. if (Handshake == null)
  429. {
  430. Handshake = new HandshakeData();
  431. if (!Handshake.Parse(packet.Payload))
  432. HTTPManager.Logger.Warning("SocketManager", "Expected handshake data, but wasn't able to parse. Payload: " + packet.Payload);
  433. (this as IManager).OnTransportConnected(Transport);
  434. return;
  435. }
  436. else
  437. HTTPManager.Logger.Information("SocketManager", "OnPacket - Already received handshake data!");
  438. break;
  439. case TransportEventTypes.Ping:
  440. if (this.Options.ServerVersion == SupportedSocketIOVersions.Unknown)
  441. {
  442. HTTPManager.Logger.Information("SocketManager", "Received Ping packet from server, setting ServerVersion to v3!");
  443. this.Options.ServerVersion = SupportedSocketIOVersions.v3;
  444. }
  445. lastPingReceived = DateTime.UtcNow;
  446. (this as IManager).SendPacket(new Packet(TransportEventTypes.Pong, SocketIOEventTypes.Unknown, "/", string.Empty));
  447. break;
  448. case TransportEventTypes.Pong:
  449. IsWaitingPong = false;
  450. break;
  451. }
  452. Socket socket = null;
  453. if (Namespaces.TryGetValue(packet.Namespace, out socket))
  454. (socket as ISocket).OnPacket(packet);
  455. else
  456. HTTPManager.Logger.Warning("SocketManager", "Namespace \"" + packet.Namespace + "\" not found!");
  457. }
  458. #endregion
  459. /// <summary>
  460. /// Sends an event to all available namespaces.
  461. /// </summary>
  462. public void EmitAll(string eventName, params object[] args)
  463. {
  464. for (int i = 0; i < Sockets.Count; ++i)
  465. Sockets[i].Emit(eventName, args);
  466. }
  467. /// <summary>
  468. /// Emits an internal packet-less event to the root namespace without creating it if it isn't exists yet.
  469. /// </summary>
  470. void IManager.EmitEvent(string eventName, params object[] args)
  471. {
  472. Socket socket = null;
  473. if (Namespaces.TryGetValue("/", out socket))
  474. (socket as ISocket).EmitEvent(eventName, args);
  475. }
  476. /// <summary>
  477. /// Emits an internal packet-less event to the root namespace without creating it if it isn't exists yet.
  478. /// </summary>
  479. void IManager.EmitEvent(SocketIOEventTypes type, params object[] args)
  480. {
  481. (this as IManager).EmitEvent(EventNames.GetNameFor(type), args);
  482. }
  483. void IManager.EmitError(SocketIOErrors errCode, string msg)
  484. {
  485. (this as IManager).EmitEvent(SocketIOEventTypes.Error, new Error(errCode, msg));
  486. }
  487. void IManager.EmitAll(string eventName, params object[] args)
  488. {
  489. for (int i = 0; i < Sockets.Count; ++i)
  490. (Sockets[i] as ISocket).EmitEvent(eventName, args);
  491. }
  492. #region IHeartbeat Implementation
  493. /// <summary>
  494. /// Called from the HTTPManager's OnUpdate function every frame. It's main function is to send out heartbeat messages.
  495. /// </summary>
  496. void IHeartbeat.OnHeartbeatUpdate(TimeSpan dif)
  497. {
  498. switch (State)
  499. {
  500. case States.Paused:
  501. // To ensure no messages are lost, the upgrade packet will only be sent once all the buffers of the existing transport are flushed and the transport is considered paused.
  502. if (!Transport.IsRequestInProgress &&
  503. !Transport.IsPollingInProgress)
  504. {
  505. State = States.Open;
  506. // Close the current transport
  507. Transport.Close();
  508. // and switch to the newly upgraded one
  509. Transport = UpgradingTransport;
  510. UpgradingTransport = null;
  511. // We will send an Upgrade("5") packet.
  512. Transport.Send(new Packet(TransportEventTypes.Upgrade, SocketIOEventTypes.Unknown, "/", string.Empty));
  513. goto case States.Open;
  514. }
  515. break;
  516. case States.Opening:
  517. if (DateTime.UtcNow - ConnectionStarted >= Options.Timeout)
  518. {
  519. (this as IManager).EmitError(SocketIOErrors.Internal, "Connection timed out!");
  520. (this as IManager).EmitEvent("connect_error");
  521. (this as IManager).EmitEvent("connect_timeout");
  522. (this as IManager).TryToReconnect();
  523. }
  524. break;
  525. case States.Reconnecting:
  526. if (ReconnectAt != DateTime.MinValue && DateTime.UtcNow >= ReconnectAt)
  527. {
  528. (this as IManager).EmitEvent("reconnect_attempt");
  529. (this as IManager).EmitEvent("reconnecting");
  530. Open();
  531. }
  532. break;
  533. case States.Open:
  534. ITransport trans = null;
  535. // Select transport to use
  536. if (Transport != null && Transport.State == TransportStates.Open)
  537. trans = Transport;
  538. // not yet open?
  539. if (trans == null || trans.State != TransportStates.Open)
  540. return;
  541. // Start to poll the server for events
  542. trans.Poll();
  543. // Start to send out unsent packets
  544. SendOfflinePackets();
  545. // First time we reached this point. Set the LastHeartbeat to the current time, 'cause we are just opened.
  546. if (LastHeartbeat == DateTime.MinValue)
  547. {
  548. LastHeartbeat = DateTime.UtcNow;
  549. lastPingReceived = DateTime.UtcNow;
  550. if (this.Options.ServerVersion == SupportedSocketIOVersions.Unknown) {
  551. (this as IManager).SendPacket(new Packet(TransportEventTypes.Ping, SocketIOEventTypes.Unknown, "/", string.Empty));
  552. IsWaitingPong = true;
  553. }
  554. return;
  555. }
  556. switch (this.Options.ServerVersion)
  557. {
  558. case SupportedSocketIOVersions.v2:
  559. // It's time to send out a ping event to the server
  560. if (!IsWaitingPong && DateTime.UtcNow - LastHeartbeat > Handshake.PingInterval)
  561. {
  562. (this as IManager).SendPacket(new Packet(TransportEventTypes.Ping, SocketIOEventTypes.Unknown, "/", string.Empty));
  563. LastHeartbeat = DateTime.UtcNow;
  564. IsWaitingPong = true;
  565. }
  566. // No pong event received in the given time, we are disconnected.
  567. if (IsWaitingPong && DateTime.UtcNow - LastHeartbeat > Handshake.PingTimeout)
  568. {
  569. IsWaitingPong = false;
  570. (this as IManager).TryToReconnect();
  571. }
  572. break;
  573. case SupportedSocketIOVersions.v3:
  574. if (DateTime.UtcNow - lastPingReceived > Handshake.PingInterval + Handshake.PingTimeout)
  575. {
  576. (this as IManager).TryToReconnect();
  577. }
  578. break;
  579. case SupportedSocketIOVersions.Unknown:
  580. var diff = DateTime.UtcNow - LastHeartbeat;
  581. if (diff > Handshake.PingTimeout)
  582. {
  583. this.Options.ServerVersion = IsWaitingPong ? SupportedSocketIOVersions.v3 : SupportedSocketIOVersions.v2;
  584. }
  585. break;
  586. }
  587. break; // case States.Open:
  588. }
  589. }
  590. #endregion
  591. }
  592. }
  593. #endif