/// <summary> /// Pop the current server and put onto the end of the list. /// Select head of list as long as number of reconnect attempts under MaxReconnect. /// </summary> /// <returns>The next server endpoint, or null</returns> internal ServerConnectionStatus SelectNextServer() { lock (this._locker) { ServerConnectionStatus s = this._currentServer; if (s == null) { return(null); } //remove the current server. this._servers.Remove(s); if (this._options.MaxReconnect > 0 && s.ReconnectionCount < this._options.MaxReconnect ) { //if we haven't surpassed max reconnects, add it to try again. this._servers.AddLast(s); } this._currentServer = this._servers.FirstOrDefault(); return(this._currentServer); } }
/// <summary> /// Creates an instance of <see cref="TcpConnection"/> using the /// specified <see cref="ServerConnectionStatus"/> as parameters. /// </summary> /// <param name="options"></param> /// <param name="s"></param> /// <param name="token"></param> /// <returns></returns> internal static TcpConnection Create( ClientOptions options, ServerConnectionStatus s, CancellationToken token ) { var instance = new TcpConnection(); instance._options = options; var client = new TcpClient(); try { //it seems there's no way to better make a connection attempt //within a certain timeout. //the solution is connecting in the async-fashion, then //controlling the task timeout and cancellation in the sync-fashion bool success = client .ConnectAsync(s.EndPoint.Address, s.EndPoint.Port) .Wait((int)options.Timeout.TotalMilliseconds, token); if (success) { //set-up the socket client.NoDelay = false; client.ReceiveBufferSize = Defaults.defaultBufSize * 2; client.SendBufferSize = Defaults.defaultBufSize; client.SendTimeout = InitialSocketSendTimeout; client.ReceiveTimeout = InitialSocketReceiveTimeout; instance._client = client; instance._netStream = client.GetStream(); // save off the hostname instance._hostName = s.EndPoint.Address.ToString(); instance.CreateReaderWriter(); } else { throw new NATSConnectionException("timeout"); } } catch (Exception ex) { client.Dispose(); throw ex; } //Console.WriteLine("socket connected"); return(instance); }
/// <summary> /// Main entry-point to connect to a server /// </summary> /// <param name="initialPresentation">Indicates whether it's the very first attempt to connect, /// or rather some retry due to a connection drop.</param> /// <param name="token">Propagates notification that operations should be canceled.</param> /// <returns>The <see cref="TcpConnection"/> useful to exchange data with the server.</returns> internal TcpConnection ConnectToAServer( bool initialPresentation, CancellationToken token ) { //select an iterator against the proper server list IEnumerator <ServerConnectionStatus> iter; lock (this._locker) { if (initialPresentation) { iter = this._servers.GetEnumerator(); } else { iter = new ServerStatusEnumerator(this); } } //task cancelation will raise an exception token.ThrowIfCancellationRequested(); Exception lastex = null; while (iter.MoveNext()) { ServerConnectionStatus s = iter.Current; try { //connection attempt var tcp = TcpConnection.Create(this._options, s, token); s.DidConnect = true; s.ReconnectionCount = 0; this._currentServer = s; return(tcp); } catch (OperationCanceledException ex) { throw ex; } catch (Exception ex) { lastex = ex; } if (initialPresentation == false) { //manage stats s.ReconnectionCount++; this.IncrementStatsCounter?.Invoke(); //waits before retrying again Helpers.CancelableDelay(this._options.ReconnectWait, token); } } if (lastex != null) { throw lastex; } this._currentServer = null; return(null); }