/// <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); }
/// <summary> /// The "inner" worker /// </summary> /// <remarks> /// This method can raise any kind of exception, which will be caught by the <see cref="OuterWorker(object)"/>. /// </remarks> /// <param name="ctx"></param> private void InnerWorker( WorkerContext ctx ) { this._info = null; /** * Initial connection to a server **/ try { this._tcp = this._srvPool.ConnectToAServer( this._workerStage == StageServerInitialConnection, ctx.CancToken ); if (this._tcp == null) { throw new Exception(); } } catch (OperationCanceledException ex) { throw ex; } catch (Exception) { throw new NATSNoServersException("Unable to connect to a server."); } /** * Waiting for the "INFO" data issued by the server **/ this._workerStage = StageWaitInfo; this.ReadThenParseSegment(null, null, ReadTimeout, ctx.CancToken); if (this._info == null) { throw new NATSConnectionException("Protocol exception, INFO not received"); } /** * Send "CONNECT" to the server **/ this._workerStage = this._options.Verbose ? StageSendConnectVerbose : StageSendConnectNormal; try { this._tcp.SocketSendTimeout = 0; this._tcp.WriteString(this.ConnectProto()); this.SendPing(); } catch (Exception ex) { throw new NATSException("Error sending connect protocol message", ex); } this.ReadThenParseSegment( ex => throw new NATSConnectionException("Connect read error", ex), null, ReadTimeout, ctx.CancToken ); if (this._workerStage != StageLogicallyConnected) { throw new NATSConnectionException("Connect read protocol error"); } /** * Main processing loop **/ this._tcp.SocketReceiveTimeout = ReadTimeout; //for safety this.SetStatus(ConnState.CONNECTED); this.PingStart(); this._owner.RequestReplyManager.Start(); int readCount = 0; while (ctx.CancToken.IsCancellationRequested == false) { if (this._writePending) { //something to send is pending lock (this._writeLocker) { //is PING, maybe? if (this._pingSendPending) { this.SendPing(); } //are one or more SUB messages, maybe? if (this._subPending) { for (int i = 0, sublen = this._subQueue.Count; i < sublen; i++) { this._tcp.WriteString(this._subQueue[i]); } this._subQueue.Clear(); this._tcp.WriteFlush(); this._subPending = false; } this._writePending = false; } } else if (this._isLogicalStarted) { if (readCount == 0) { //no operation was perfomed: no data in, no data out. //the cycle would be too tight, then. //give the OS a breathe of fresh air... Thread.Sleep(1); } } else { //the initial NATS handshake with the server is complete: //if the start was async, then consider its task as terminated this._isLogicalStarted = true; ctx.TaskCompletion?.SetResult(null); } //listen to incoming data readCount = this.ReadThenParseSegment( this.ProcessOpError, this.ProcessOpError, 0, ctx.CancToken ); } }