/// <summary> /// Reestablish connection to server. /// </summary> /// <remarks> /// This method checks if the session is valid. If not, a new logon in perfomed automatically. /// Handle the NewLogonNeeded event to provide credentials. /// </remarks> /// <returns>True, if reconnecting was successfull, otherwis false </returns> internal bool InternalReconnect() { // When the session isn´t valid, the server process must have been restarted if (!RemoteDispatcher.ExistSession(_sessionID)) { AuthCredentials credentials = null; var performNewLogon = true; // If cached auto login credentials are present if (_autoLoginOnExpiredSession) { credentials = _autoLoginCredentials; } else { var newLogonNeededEventArgs = new NewLogonNeededEventArgs(); if (OnNewLogonNeeded(newLogonNeededEventArgs)) { performNewLogon = !newLogonNeededEventArgs.Cancel; credentials = newLogonNeededEventArgs.Credentials; } else { performNewLogon = false; } } if (performNewLogon) { if (credentials == null) { credentials = new AuthCredentials(); } credentials.Authenticate(_sessionID, RemoteDispatcher); ReconnectRemoteEvents(); RemoteDispatcher.ReceiveClientHeartbeat(_sessionID); return(true); } } else { RemoteDispatcher.ReceiveClientHeartbeat(_sessionID); return(true); } return(false); }
/// <summary> /// Will be called from session keep alive timer on ervery interval. /// </summary> /// <param name="state">State (not used)</param> private void KeepSessionAlive(object state) { try { PrepareCallContext(false); int serverSessionAgeLimit = RemoteDispatcher.RenewSession(); if (_sessionAgeLimit != serverSessionAgeLimit) { _sessionAgeLimit = serverSessionAgeLimit; StartKeepSessionAliveTimer(); } } catch { } }
/// <summary> /// Reconnects to all remote events or delegates of any known proxy for this connection, after a server restart. /// </summary> /// <remarks> /// Does not check, if the event handler registrations are truly lost. /// </remarks> private void ReconnectRemoteEventsCore() { if (_isDisposed) { return; } try { lock (RemoteEventsLock) { var subscriptions = AliveProxies.Select(p => p.GetActiveSubscriptions()).Where(s => !s.DelegateCorrelationSet.IsNullOrEmpty()).ToArray(); var correlationSets = subscriptions.SelectMany(s => s.DelegateCorrelationSet).ToArray(); _localSubscriptionTracker.Reset(correlationSets); PrepareCallContext(false); RemoteDispatcher.ReconnectEventHandlers(subscriptions); } } catch (Exception ex) { Trace.WriteLine("Error while restoring client subscriptions: {0}", ex); } }
/// <summary> /// Will be called from polling timer on every interval. /// </summary> /// <param name="state">State (not used)</param> internal void SendHeartbeat(object state) { if (_sendingHeartbeat) { // if polling timer interval is less than // channel timeout, skip sending a heartbeat return; } try { _sendingHeartbeat = true; try { PrepareCallContext(false); RemoteDispatcher.ReceiveClientHeartbeat(_sessionID); } finally { _sendingHeartbeat = false; } } catch (Exception ex) { PollingEnabled = false; bool problemSolved = false; DisconnectedEventArgs e = new DisconnectedEventArgs() { Exception = ex, RetryCount = 0, Retry = false }; OnDisconnected(e); while (e.Retry) { e.Retry = false; Thread.Sleep(Convert.ToInt32(_pollingInterval.TotalMilliseconds)); try { problemSolved = InternalReconnect(); } catch (Exception retryEx) { e.Exception = retryEx; e.RetryCount++; OnDisconnected(e); } } if (problemSolved) { PollingEnabled = true; OnReconnected(EventArgs.Empty); } else { // connection wasn't restored, make sure // that the polling timer is stopped PollingEnabled = false; } } }
/// <summary> /// Releases unmanaged and — optionally — managed resources. /// </summary> /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> protected virtual void Dispose(bool disposing) { if (!disposing) { return; } if (!_isDisposed) { _isDisposed = true; if (_pollingTimer != null) { _pollingTimer.Dispose(); _pollingTimer = null; } _pollingEnabled = false; if (_keepSessionAliveTimer != null) { _keepSessionAliveTimer.Dispose(); _keepSessionAliveTimer = null; } try { foreach (var zyanProxy in AliveProxies) { zyanProxy.RemoveAllRemoteEventHandlers(); } RemoteDispatcher.Logoff(_sessionID); } catch (RemotingException) { } catch (SocketException) { } catch (WebException) { } catch (MessageException) { } catch (Exception ex) { Trace.WriteLine("Unexpected exception of type {0} caught while disposing ZyanConnection: {1}", ex.GetType(), ex.Message); } finally { lock (_connections) { _connections.Remove(this); } } if (_remotingChannel != null) { // unsubscribe from connection notifications var connectionNotification = _remotingChannel as IConnectionNotification; if (connectionNotification != null) { connectionNotification.ConnectionEstablished -= Channel_ConnectionEstablished; } // unregister remoting channel var registeredChannel = ChannelServices.GetChannel(_remotingChannel.ChannelName); if (registeredChannel != null && registeredChannel == _remotingChannel) { ChannelServices.UnregisterChannel(_remotingChannel); } // dispose remoting channel, if it's disposable var disposableChannel = _remotingChannel as IDisposable; if (disposableChannel != null) { disposableChannel.Dispose(); } _remotingChannel = null; } _remoteDispatcher = null; _serverUrl = string.Empty; _sessionID = Guid.Empty; _protocolSetup = null; _serializationHandling = null; _componentHostName = string.Empty; if (_registeredComponents != null) { _registeredComponents.Clear(); _registeredComponents = null; } if (_callInterceptors != null) { _callInterceptors.Clear(); _callInterceptors = null; } if (_autoLoginCredentials != null) { _autoLoginCredentials = null; } GC.WaitForPendingFinalizers(); } }
/// <summary> /// Creates a new instance of the ZyanConnection class. /// </summary> /// <param name="serverUrl">URL of remote server (e.G. "tcp://server1:46123/myapp").</param> /// <param name="protocolSetup">Protocol and communication settings.</param> /// <param name="credentials">Login credentials.</param> /// <param name="autoLoginOnExpiredSession">Specifies whether the proxy should relogin automatically when the session expired.</param> /// <param name="keepSessionAlive">Specifies whether the session should be automaticly kept alive.</param> public ZyanConnection(string serverUrl, IClientProtocolSetup protocolSetup, AuthCredentials credentials, bool autoLoginOnExpiredSession, bool keepSessionAlive) { if (string.IsNullOrEmpty(serverUrl)) { throw new ArgumentException(LanguageResource.ArgumentException_ServerUrlMissing, "serverUrl"); } if (protocolSetup == null) { // try to select the protocol automatically protocolSetup = ClientProtocolSetup.GetClientProtocol(serverUrl); if (protocolSetup == null) { throw new ArgumentNullException("protocolSetup"); } } if (!protocolSetup.IsUrlValid(serverUrl)) { throw new ArgumentException(LanguageResource.ArgumentException_ServerUrlIsInvalid, "serverUrl"); } _proxies = new List <WeakReference>(); _protocolSetup = protocolSetup; _sessionID = Guid.NewGuid(); _serverUrl = serverUrl; _autoLoginOnExpiredSession = autoLoginOnExpiredSession; _keepSessionAlive = keepSessionAlive; if (_autoLoginOnExpiredSession) { _autoLoginCredentials = credentials; } _serializationHandling = new SerializationHandlerRepository(); CallInterceptionEnabled = false; _callInterceptors = new CallInterceptorCollection(); RegisterStandardSerializationHandlers(); string[] addressParts = _serverUrl.Split('/'); _componentHostName = addressParts[addressParts.Length - 1]; _remotingChannel = _protocolSetup.CreateChannel(); if (!ZyanSettings.DisableUrlRandomization) { _remotingChannel = ChannelWrapper.WrapChannel(_remotingChannel, _protocolSetup.ChannelName); } if (_remotingChannel != null) { var registeredChannel = ChannelServices.GetChannel(_remotingChannel.ChannelName); if (registeredChannel == null) { ChannelServices.RegisterChannel(_remotingChannel, false); } else { _remotingChannel = registeredChannel; } } else { throw new ApplicationException(LanguageResource.ApplicationException_NoChannelCreated); } var connectionNotification = _remotingChannel as IConnectionNotification; if (connectionNotification != null) { connectionNotification.ConnectionEstablished += Channel_ConnectionEstablished; } string channelName = _remotingChannel.ChannelName; if (credentials == null) { credentials = new AuthCredentials(); } try { credentials.Authenticate(_sessionID, RemoteDispatcher); _registeredComponents = new List <ComponentInfo>(RemoteDispatcher.GetRegisteredComponents()); _sessionAgeLimit = RemoteDispatcher.SessionAgeLimit; } catch (Exception ex) { // unregister remoting channel var registeredChannel = ChannelServices.GetChannel(channelName); if (registeredChannel != null) { ChannelServices.UnregisterChannel(registeredChannel); } // dispose channel if it's disposable var disposableChannel = registeredChannel as IDisposable; if (disposableChannel != null) { disposableChannel.Dispose(); } _remotingChannel = null; _remoteDispatcher = null; throw ex.PreserveStackTrace(); } var reconnectEvents = new Action(ReconnectRemoteEventsCore); var debounceInterval = ZyanSettings.ReconnectRemoteEventsDebounceInterval.TotalMilliseconds; ReconnectRemoteEvents = reconnectEvents.Debounce((int)debounceInterval); StartKeepSessionAliveTimer(); lock (_connections) { _connections.Add(this); } }
/// <summary> /// Refreshes the list of server-side components. /// </summary> public void RefreshRegisteredComponents() { _registeredComponents = new List <ComponentInfo>(RemoteDispatcher.GetRegisteredComponents()); }