Intent: We want to have one transport shared among all publishers and subscribers, in this process, if they happen to use the same TCP/IP port.
Inheritance: INetMqTransportShared
        /// <summary>
        /// Intent: Singleton.
        /// </summary>
        public static NetMqTransportShared Instance(Action <string> loggerDelegate = null)
        {
            if (_instance == null)
            {
                lock (_syncRoot)
                {
                    if (_instance == null)
                    {
                        _instance = new NetMqTransportShared(loggerDelegate);
                    }
                }
            }

            return(_instance);
        }
		/// <summary>
		/// Intent: Singleton.
		/// </summary>
		public static NetMqTransportShared Instance(Action<string> loggerDelegate = null)
		{
			if (_instance == null)
			{
				lock (_syncRoot)
				{
					if (_instance == null)
					{
						_instance = new NetMqTransportShared(loggerDelegate);
					}
				}
			}

			return _instance;
		}
Example #3
0
 private void InitializePublisherOnFirstUse(string addressZeroMq)
 {
     if (_initializePublisherDone == false)             // Double checked locking.
     {
         lock (_initializePublisherLock)
         {
             if (_initializePublisherDone == false)
             {
                 _loggerDelegate?.Invoke(string.Format("Publisher socket binding to: {0}\n", AddressZeroMq));
                 _publisherSocket         = NetMqTransportShared.Instance(_loggerDelegate).GetSharedPublisherSocket(addressZeroMq);
                 _initializePublisherDone = true;
             }
         }
     }
 }
        private void InitializeSubscriberOnFirstUse(string addressZeroMq)
        {
            if (_initializeSubscriberDone == false) // Double checked locking.
            {
                lock (_subscribersLock)
                {
                    if (_initializeSubscriberDone == false)
                    {
                        if (_cancellationTokenSource == null)
                        {
                            _cancellationTokenSource = new CancellationTokenSource();
                        }

                        _subscriberSocket =
                            NetMqTransportShared.Instance(_loggerDelegate).GetSharedSubscriberSocket(addressZeroMq);

                        ManualResetEvent threadReadySignal = new ManualResetEvent(false);

                        _thread = new Thread(() =>
                        {
                            try
                            {
                                _loggerDelegate?.Invoke(string.Format("Thread initialized.\n"));
                                threadReadySignal.Set();
                                while (_cancellationTokenSource.IsCancellationRequested == false)
                                {
                                    //_loggerDelegate?.Invoke(string.Format("Received message for {0}.\n", typeof(T)));

                                    string messageTopicReceived = _subscriberSocket.ReceiveFrameString();
                                    if (messageTopicReceived != SubscriberFilterName)
                                    {
                                        // This message is for another subscriber. This should never occur.
#if DEBUG
                                        throw new Exception(
                                            "Error E38444. Internal exception, this should never occur, as the ZeroMQ lib automaticlaly filters by subject name.");
#else
                                        return;
#endif
                                    }
                                    var type = _subscriberSocket.ReceiveFrameString();
                                    switch (type)
                                    {
                                    // Originated from "OnNext".
                                    case "N":
                                        T messageReceived = _serializer.Deserialize(_subscriberSocket.ReceiveFrameBytes());
                                        lock (_subscribersLock)
                                        {
                                            try
                                            {
                                                _subscribers.ForEach(o => o.OnNext(messageReceived));
                                            }
                                            catch (Exception ex)
                                            {
                                                // If an unhandled exception is thrown inside the clients OnNext() event, we don't want this thread to die!
                                                _loggerDelegate?.Invoke(string.Format("Exception thrown on Subscription OnNext handler. Suggest adding an exception handler. Ignoring. Exception: {0}\n", ex));
                                            }
                                        }
                                        break;

                                    // Originated from "OnCompleted".
                                    case "C":
                                        lock (_subscribersLock)
                                        {
                                            try
                                            {
                                                _subscribers.ForEach(o => o.OnCompleted());
                                            }
                                            catch (Exception ex)
                                            {
                                                // If an unhandled exception is thrown inside the clients OnCompleted() event, we don't want this thread to die!
                                                _loggerDelegate?.Invoke(string.Format("Exception thrown on Subscription OnCompleted handler. Suggest adding an exception handler. Ignoring. Exception: {0}\n", ex));
                                            }

                                            // We are done! We don't want to send any more messages to subscribers, and we
                                            // want to close the listening socket.
                                            _cancellationTokenSource.Cancel();
                                        }
                                        break;

                                    // Originated from "OnException".
                                    case "E":
                                        Exception exception = null;
                                        try
                                        {
                                            // Not used, but useful for cross-platform debugging: we can read the error straight off the wire.
                                            string exceptionAsXml = _subscriberSocket.ReceiveFrameString();
                                            exception             = new Exception(exceptionAsXml);
                                            _subscriberSocket.ReceiveFrameBytes();     // For future expansion for a machine readable exception.
                                        }
                                        catch (Exception ex)
                                        {
                                            // If we had trouble deserializing the exception (probably due to a
                                            // different version of .NET), then do the next best thing: (1) The
                                            // inner exception is the error we got when deserializing, and (2) the
                                            // main exception is the human-readable "exception.ToString()" that we
                                            // originally captured.
                                            exception = new Exception("Error deserializing exception. To fix, recompile the remote sender with the same version as this subscriber.", ex);
                                        }

                                        lock (_subscribersLock)
                                        {
                                            try
                                            {
                                                _subscribers.ForEach(o => o.OnError(exception));
                                            }
                                            catch (Exception ex)
                                            {
                                                // If an unhandled exception is thrown inside the clients OnCompleted() event, we don't want this thread to die!
                                                _loggerDelegate?.Invoke(string.Format("Exception thrown in Subscription OnError handler. Suggest adding an exception handler. Ignoring. Exception: {0}\n", ex));
                                            }
                                        }
                                        break;

                                    // Originated from a "Ping" request.
                                    case "P":
                                        // Do nothing, this is a ping command used to wait until sockets are initialized properly.
                                        _loggerDelegate?.Invoke(string.Format("Received ping.\n"));
                                        break;

                                    default:
                                        throw new Exception(string.Format("Error E28734. Something is wrong - received '{0}' when we expected \"N\", \"C\" or \"E\" - are we out of sync?", type));
                                    }
                                }
                            }
                            catch (Exception ex)
                            {
                                _loggerDelegate?.Invoke(string.Format("Error E23844. Exception in threadName \"{0}\". Thread exiting. Exception: \"{1}\".\n", SubscriberFilterName, ex.Message));
                                lock (_subscribersLock)
                                {
                                    this._subscribers.ForEach((ob) => ob.OnError(ex));
                                }
                            }
                            finally
                            {
                                lock (_subscribersLock)
                                {
                                    _subscribers.Clear();
                                }
                                _cancellationTokenSource.Dispose();

                                // Disconnect from the socket.
                                _subscriberSocket.Dispose();

                                _threadWaitExitHandle.Set();
                            }
                        })
                        {
                            Name         = this.SubscriberFilterName,
                            IsBackground = true                             // Have to set it to background, or else it will not exit when the program exits.
                        };
                        _thread.Start();

                        // Wait for subscriber thread to properly spin up.
                        threadReadySignal.WaitOne(TimeSpan.FromMilliseconds(3000));

                        // Intent: Now connect to the socket.
                        {
                            _loggerDelegate?.Invoke(string.Format("Subscriber socket connecting to: {0}\n", addressZeroMq));

                            // this.SubscriberFilterName is set to the type T of the incoming class by default, so we can
                            // have many types on the same transport.
                            _subscriberSocket.Subscribe(this.SubscriberFilterName);
                        }

                        _loggerDelegate?.Invoke(string.Format("Subscriber: finished setup.\n"));

                        _initializeSubscriberDone = true;
                    }
                }                  // lock
                Thread.Sleep(500); // Otherwise, the first item we subscribe  to may get missed by the subscriber.
            }
        }