/// <summary> /// Subscribes (or re-subscribes) to a data publisher for a set of data points. /// </summary> /// <param name="remotelySynchronized">Boolean value that determines if subscription should be remotely synchronized - note that data publisher may not allow remote synchronization.</param> /// <param name="compactFormat">Boolean value that determines if the compact measurement format should be used. Set to <c>false</c> for full fidelity measurement serialization; otherwise set to <c>true</c> for bandwidth conservation.</param> /// <param name="connectionString">Connection string that defines required and optional parameters for the subscription.</param> /// <returns><c>true</c> if subscribe transmission was successful; otherwise <c>false</c>.</returns> public virtual bool Subscribe(bool remotelySynchronized, bool compactFormat, string connectionString) { bool success = false; if (!string.IsNullOrWhiteSpace(connectionString)) { try { // Parse connection string to see if it contains a data channel definition Dictionary<string, string> settings = connectionString.ParseKeyValuePairs(); UdpClient dataChannel = null; string setting; // Track specified time inclusion for later deserialization if (settings.TryGetValue("includeTime", out setting)) m_includeTime = setting.ParseBoolean(); else m_includeTime = true; settings.TryGetValue("dataChannel", out setting); if (!string.IsNullOrWhiteSpace(setting)) { dataChannel = new UdpClient(setting); dataChannel.ReceiveBufferSize = ushort.MaxValue; dataChannel.MaxConnectionAttempts = -1; dataChannel.ConnectAsync(); } // Assign data channel client reference and attach to needed events DataChannel = dataChannel; // Setup subscription packet using (BlockAllocatedMemoryStream buffer = new BlockAllocatedMemoryStream()) { DataPacketFlags flags = DataPacketFlags.NoFlags; byte[] bytes; if (remotelySynchronized) flags |= DataPacketFlags.Synchronized; if (compactFormat) flags |= DataPacketFlags.Compact; // Write data packet flags into buffer buffer.WriteByte((byte)flags); // Get encoded bytes of connection string bytes = m_encoding.GetBytes(connectionString); // Write encoded connection string length into buffer buffer.Write(BigEndian.GetBytes(bytes.Length), 0, 4); // Encode connection string into buffer buffer.Write(bytes, 0, bytes.Length); // Cache subscribed synchronization state m_synchronizedSubscription = remotelySynchronized; // Send subscribe server command with associated command buffer success = SendServerCommand(ServerCommand.Subscribe, buffer.ToArray()); } } catch (Exception ex) { OnProcessException(new InvalidOperationException("Exception occurred while trying to make publisher subscription: " + ex.Message, ex)); } } else OnProcessException(new InvalidOperationException("Cannot make publisher subscription without a connection string.")); // Reset decompressor on successful resubscription if (success && (object)m_decompressionBlock != null) m_decompressionBlock.Reset(); return success; }
/// <summary> /// Releases a reference to this client's shared client, /// and disposes of the shared client if nobody is using it. /// </summary> private void ReturnSharedClient() { EndPoint localEndPoint; lock (s_sharedClients) { if ((object)m_udpClient == null) return; // Detach from event handlers m_udpClient.ConnectionAttempt -= SharedClient_ConnectionAttempt; m_udpClient.ConnectionEstablished -= SharedClient_ConnectionEstablished; m_udpClient.ConnectionException -= SharedClient_ConnectionException; m_udpClient.ConnectionTerminated -= SharedClient_ConnectionTerminated; m_udpClient.ReceiveDataException -= SharedClient_ReceiveDataException; m_udpClient.ReceiveDataFrom -= SharedClient_ReceiveDataFrom; m_udpClient.SendDataException -= SharedClient_SendDataException; // Decrement reference count localEndPoint = GetLocalEndPoint(); s_sharedReferenceCount[localEndPoint]--; if (s_sharedReferenceCount[localEndPoint] == 0) { // No more references to UDP client // exist so dispose of it m_udpClient.Disconnect(); m_udpClient.Dispose(); s_sharedClients.Remove(localEndPoint); s_sharedReferenceCount.Remove(localEndPoint); } // Release reference to UDP client m_udpClient = null; } }
/// <summary> /// Terminates the client as quickly as possible and /// removes it from the collection of shared clients. /// </summary> private void TerminateSharedClient() { EndPoint localEndPoint; UdpClient sharedClient; lock (s_sharedClients) { localEndPoint = GetLocalEndPoint(); if (s_sharedClients.TryGetValue(localEndPoint, out sharedClient)) { // If the wrapped client and the shared client are the same, // no one has terminated the shared client yet if (m_udpClient == sharedClient) { // Disconnect and dispose of the old client m_udpClient.Disconnect(); m_udpClient.Dispose(); // Remove the old client from the // collection of shared clients localEndPoint = GetLocalEndPoint(); s_sharedClients.Remove(localEndPoint); s_sharedReferenceCount.Remove(localEndPoint); } } m_udpClient = null; } }
/// <summary> /// Gets a reference to the shared client listening /// on this client's local end point. /// </summary> /// <returns>A reference to a shared client.</returns> private void GetSharedClient() { const string ConfigurationMismatchError = "Configuration mismatch detected between parsers using shared UDP client: {0}"; bool sharing; EndPoint localEndPoint; UdpClient sharedClient; lock (s_sharedClients) { localEndPoint = GetLocalEndPoint(); sharing = s_sharedClients.TryGetValue(localEndPoint, out sharedClient); if (sharing) { // Validate settings to ensure that they match if (sharedClient.ReceiveBufferSize != m_receiveBufferSize) throw new InvalidOperationException(string.Format(ConfigurationMismatchError, "Receive buffer size")); if (sharedClient.MaxConnectionAttempts != m_maxConnectionAttempts) throw new InvalidOperationException(string.Format(ConfigurationMismatchError, "Max connection attempts")); } else { // Create new client and add it to the shared list sharedClient = new UdpClient(); s_sharedClients.Add(localEndPoint, sharedClient); s_sharedReferenceCount.Add(localEndPoint, 0); } // Set the UDP client member variable ASAP to guarantee // that it is set before callbacks can be triggered m_udpClient = sharedClient; // Attach to event handlers sharedClient.ConnectionAttempt += SharedClient_ConnectionAttempt; sharedClient.ConnectionEstablished += SharedClient_ConnectionEstablished; sharedClient.ConnectionException += SharedClient_ConnectionException; sharedClient.ConnectionTerminated += SharedClient_ConnectionTerminated; sharedClient.ReceiveDataException += SharedClient_ReceiveDataException; sharedClient.ReceiveDataFrom += SharedClient_ReceiveDataFrom; sharedClient.SendDataException += SharedClient_SendDataException; sharedClient.UnhandledUserException += SharedClient_UnhandledUserException; // Make sure to set packet info flag if needed since // other SharedUdpClientReferences may not need it if (m_receivePacketInfo) sharedClient.ReceivePacketInfo = true; if (!sharing) { // Initialize settings and connect sharedClient.ConnectionString = m_connectionString; sharedClient.ReceiveBufferSize = m_receiveBufferSize; sharedClient.MaxConnectionAttempts = m_maxConnectionAttempts; sharedClient.ConnectAsync(); } // Increment reference count s_sharedReferenceCount[localEndPoint]++; } if (sharing && sharedClient.CurrentState == ClientState.Connected) OnConnectionEstablished(); }