/// <summary> /// Implements IOutboundTransport.UnsubscribeClient /// </summary> public void UnsubscribeClient(string clientId) { if (_disposed) { throw new ObjectDisposedException(GetType().FullName); } if (string.IsNullOrEmpty(clientId)) { throw new ArgumentNullException("clientId"); } TlsClient client = null; _listLock.AcquireReaderLock(DEFAULT_JOIN_TIMEOUT); try { if (!_clients.ContainsKey(clientId)) { throw new TransportException("Client not subscribed to transport"); } client = _clients[clientId]; //Remove LockCookie ck = _listLock.UpgradeToWriterLock(DEFAULT_JOIN_TIMEOUT); try { _clients.Remove(clientId); } finally { _listLock.DowngradeFromWriterLock(ref ck); } } finally { _listLock.ReleaseReaderLock(); if (client != null) { try { client.Stream.Flush(); } catch { } finally { client.Dispose(); } } } }
private void CleanupClients(object state) { List <string> toRemove = new List <string>(); /* Scan for dead clients * According to MSDN, a client will be detected to be dead * only after a failed I/O. * If no messages run on channel, lots of dead clients won't be * discovered. This is why we force flushing the stream: if client * is dead, IOException is triggered and Connected is set to false */ _listLock.AcquireReaderLock(DEFAULT_JOIN_TIMEOUT); try { foreach (KeyValuePair <string, TlsClient> kvp in _clients) { try { kvp.Value.Stream.Flush(); } catch (IOException) { } //Flush did its real job if (!kvp.Value.Client.Connected) { toRemove.Add(kvp.Key); } } if (toRemove.Count > 0) { LockCookie ck = _listLock.UpgradeToWriterLock(DEFAULT_JOIN_TIMEOUT); try { foreach (string id in toRemove) { try { TlsClient client = _clients[id]; _clients.Remove(id); client.Dispose(); } catch (KeyNotFoundException ex) { //Strange Log.Debug("Error occurred when purging dead TLS client: {0}", ex.Message); } } } finally { _listLock.DowngradeFromWriterLock(ref ck); } } } finally { _listLock.ReleaseReaderLock(); } if (toRemove.Count > 0) { Log.Notice("TLS transport {0} cleaned up {1} dead clients", GetHashCode(), toRemove.Count); } }