/// <summary>
        /// <para>Receives messages of a Sender having the same handle.</para>
        /// <para>When using the end point manager service, some security measures are advised.</para>
        /// <para>You can use Shared.Encrypt(...) (and Shared.Decrypt(...)) to encrypt your received messages (if they are strings) via the MessageReceived event.</para>
        /// <para>Alternatively you can use a ssh tunnel, that will probably be safer and faster</para>
        /// </summary>
        /// <param name="handle">
        /// <para>The handle is a value shared by a Sender and its Receivers.  ; , * + and - cannot be used!</para>
        /// <para>It links both parties so messages from a Sender get to the right Receivers.</para>
        /// <para>Make sure this is a unique value: use a GUID for instance:</para>
        /// <para>There is absolutely no checking to see if this handle is used in another Sender - Receivers relation.</para>
        /// </param>
        /// <param name="ipAddressToRegister">
        /// <para>This parameter is only useful if you are using an end point manager service.</para>
        /// <para>A receiver listens to all available IPs for connections. The ip that is registered on the end point manager (service) is by default automatically determined.</para>
        /// <para>However, this does not take into account that senders, receiver or end point manager services are possibly not on the same network.</para>
        /// <para>Therefor you can override this behaviour by supplying your own IP that will be registered to the end point manager service.</para>
        /// </param>
        /// <param name="allowedPorts">
        /// <para>This parameter is only useful if you are using an end point manager service.</para>
        /// <para>To make firewall settings easier, you can specify a pool of TCP ports where the receiver can choose one from to listen on. If none of these ports are available, this will fail.</para>
        /// <para>If you don't use this parameter, one of the total available ports on the system will be chosen.</para>
        /// </param>
        /// <param name="endPointManagerServiceConnection">
        /// <para>This is an optional parameter.</para>
        /// <para>If you don't use it, receiver end points are stored in the Windows registry and IPC communication is only possible for processes running under the current local user.</para>
        /// <para>If you do use it, these end points are fetched from a Windows service over tcp, making it a distributed IPC.This however will be slower and implies a security risk since there will be network traffic.</para>
        /// </param>
        public Receiver(string handle, IPAddress ipAddressToRegister = null, int[] allowedPorts = null, EndPointManagerServiceConnection endPointManagerServiceConnection = null)
        {
            if (string.IsNullOrWhiteSpace(handle))
            {
                throw new ArgumentNullException(handle);
            }
            if (handle.Contains(";") || handle.Contains(",") || handle.Contains("*") || handle.Contains("+") || handle.Contains("-"))
            {
                throw new ArgumentNullException(handle);
            }

            Handle = handle;
            EndPointManagerServiceConnection = endPointManagerServiceConnection;

            for (int i = 1; i != 21; i++) //Try 20 times, for when the same port is chosen by another application.
            {
                try {
                    _tcpReceiver = new TcpListener(EndPointManager.RegisterReceiver(Handle, ipAddressToRegister, allowedPorts, EndPointManagerServiceConnection));
                    _tcpReceiver.Start(endPointManagerServiceConnection == null ? 1 : 2); //Keep one connection open to enable the service pinging it.
                    break;
                }
                catch (EndPointManagerServiceConnectionException) {
                    throw;
                }
            }
        /// <summary>
        /// The sender must use this to be able to send data to the correct receivers.
        /// </summary>
        /// <param name="handle">
        /// <para>The handle is a value shared by a Sender and its Receivers.</para>
        /// <para>It links both parties so messages from a Sender get to the right Receivers.</para>
        /// <para>Make sure this is a unique value: use a GUID for instance:</para>
        /// <para>There is absolutely no checking to see if this handle is used in another Sender - Receivers relation.</para>
        /// </param>
        /// <param name="endPointManagerServiceConnection"></param>
        /// <returns></returns>
        internal static List <IPEndPoint> GetReceiverEndPoints(string handle, EndPointManagerServiceConnection endPointManagerServiceConnection)
        {
            var endPoints = new List <IPEndPoint>();

            var allEndPoints = GetRegisteredEndPoints(endPointManagerServiceConnection);

            //Only cleanup if there is no epm service.
            if (endPointManagerServiceConnection == null)
            {
                CleanupEndPoints(allEndPoints, true);
            }

            if (allEndPoints.ContainsKey(handle))
            {
                var dic = allEndPoints[handle];
                foreach (string ip in dic.Keys)
                {
                    var ipAddress = IPAddress.Parse(ip);
                    foreach (int port in dic[ip])
                    {
                        endPoints.Add(new IPEndPoint(ipAddress, port));
                    }
                }
            }

            return(endPoints);
        }
Exemple #3
0
        /// <summary>
        /// <para>Add a new Sender in the code of the process you want to send messages. Make sure the handles matches the one of the Receivers.</para>
        /// <para>When using the end point manager service, some security measures are advised.</para>
        /// <para>You can use Shared.Encrypt(...) (and Shared.Decrypt(...)) to encrypt messages (if they are strings) before sending them.</para>
        /// <para>Alternatively you can use a ssh tunnel, that will probably be safer and faster</para>
        /// <para>Suscribe to OnSendFailed for error handeling. Please note: Sending will always fail when a Receiver disappears.</para>
        /// </summary>
        /// <param name="handle">
        /// <para>The handle is a value shared by a Sender and its Receivers.  ; , * + and - cannot be used!</para>
        /// <para>It links both parties so messages from a Sender get to the right Receivers.</para>
        /// <para>Make sure this is a unique value: use a GUID for instance:</para>
        /// <para>There is absolutely no checking to see if this handle is used in another Sender - Receivers relation.</para>
        /// </param>
        /// <param name="endPointManagerServiceConnection">
        /// <para>This is an optional parameter.</para>
        /// <para>If you don't use it, receiver end points are stored in the Windows registry and IPC communication is only possible for processes running under the current local user.</para>
        /// <para>If you do use it, these end points are fetched from a Windows service over tcp, making it a distributed IPC.This however will be slower and implies a security risk since there will be network traffic.</para>
        /// </param>
        /// <param name="buffered">
        /// <para>When true, a message (+ encapsulation) you send is kept in memory. When you resend the same message it will not be serialized again.</para>
        /// <para>This buffer can ony hold one message. Using this will make sending messages faster and will take up more memory. Use this wisely for large messages.</para>
        /// </param>
        public Sender(string handle, EndPointManagerServiceConnection endPointManagerServiceConnection = null, bool buffered = false)
        {
            if (string.IsNullOrWhiteSpace(handle))
            {
                throw new ArgumentNullException(handle);
            }
            if (handle.Contains(";") || handle.Contains(",") || handle.Contains("*") || handle.Contains("+") || handle.Contains("-"))
            {
                throw new ArgumentNullException(handle);
            }

            Handle   = handle;
            Buffered = buffered;
            EndPointManagerServiceConnection = endPointManagerServiceConnection;

            _tcpSenders = new ConcurrentDictionary <TcpClient, IPEndPoint>();
            _bf         = new BinaryFormatter();
        }
        /// <summary>
        /// Add a new tcp port to the endpoints for a receiver.
        /// </summary>
        /// <param name="handle">
        /// <para>The handle is a value shared by a Sender and its Receivers.  ; , * + and - cannot be used!</para>
        /// <para>It links both parties so messages from a Sender get to the right Receivers.</para>
        /// <para>Make sure this is a unique value: use a GUID for instance:</para>
        /// <para>There is absolutely no checking to see if this handle is used in another Sender - Receivers relation.</para>
        /// </param>
        /// <param name="ipAddressToRegister">
        /// <para>This parameter is only useful if you are using an end point manager service.</para>
        /// <para>A receiver listens to all available IPs for connections. The ip that is registered on the end point manager (service) is by default automatically determined.</para>
        /// <para>However, this does not take into account that senders, receiver or end point manager services are possibly not on the same network.</para>
        /// <para>Therefor you can override this behaviour by supplying your own IP that will be registered to the end point manager service.</para>
        /// </param>
        /// <param name="allowedPorts">
        /// <para>This parameter is only useful if you are using an end point manager service.</para>
        /// <para>To make firewall settings easier, you can specify a pool of TCP ports where the receiver can choose one from to listen on. If none of these ports are available, this will fail.</para>
        /// <para>If you don't use this parameter, one of the total available ports on the system will be chosen.</para>
        /// </param>
        /// <param name="endPointManagerServiceConnection">
        /// <para>This is an optional parameter.</para>
        /// <para>If you don't use it, receiver end points are stored in the Windows registry and IPC communication is only possible for processes running under the current local user.</para>
        /// <para>If you do use it, these end points are fetched from a Windows service over tcp, making it a distributed IPC.This however will be slower and implies a security risk since there will be network traffic.</para>
        /// </param>
        /// <returns></returns>
        internal static IPEndPoint RegisterReceiver(string handle, IPAddress ipAddressToRegister, int[] allowedPorts, EndPointManagerServiceConnection endPointManagerServiceConnection)
        {
            if (string.IsNullOrWhiteSpace(handle))
            {
                throw new ArgumentNullException(handle);
            }

            IPEndPoint endPoint = null;

            if (ipAddressToRegister == null)
            {
                foreach (var ipCandidate in Shared.GetIPs())
                {
                    if (!ipCandidate.Equals(IPAddress.Loopback) && !ipCandidate.Equals(IPAddress.IPv6Loopback) &&
                        (ipCandidate.AddressFamily == AddressFamily.InterNetwork || ipCandidate.AddressFamily == AddressFamily.InterNetworkV6))
                    {
                        ipAddressToRegister = ipCandidate;
                        break;
                    }
                }
            }

            string ip = ipAddressToRegister.ToString();

            var endPoints = GetRegisteredEndPoints(endPointManagerServiceConnection);

            if (endPointManagerServiceConnection == null)
            {
                CleanupEndPoints(endPoints, false);
            }
            if (!endPoints.ContainsKey(handle))
            {
                endPoints.Add(handle, new Dictionary <string, HashSet <int> >());
            }
            if (!endPoints[handle].ContainsKey(ip))
            {
                endPoints[handle].Add(ip, new HashSet <int>());
            }

            endPoint = new IPEndPoint(ipAddressToRegister, GetAvailableTcpPort(allowedPorts));
            endPoints[handle][ip].Add(endPoint.Port);

            SetRegisteredEndPoints(endPoints, endPointManagerServiceConnection);

            return(endPoint);
        }
        /// <summary>
        /// Set endpoints to the Windows Registry (current user, volatile) or the end point manager service, if any.
        /// </summary>
        /// <param name="endPoints"></param>
        /// <param name="endPointManagerServiceConnection"></param>
        private static void SetRegisteredEndPoints(Dictionary <string, Dictionary <string, HashSet <int> > > endPoints, EndPointManagerServiceConnection endPointManagerServiceConnection)
        {
            var sb = new StringBuilder();

            foreach (string handle in endPoints.Keys)
            {
                sb.Append(handle);
                sb.Append('*');

                var ips = endPoints[handle];
                foreach (string ip in ips.Keys)
                {
                    sb.Append(ip);
                    sb.Append('-');

                    foreach (int port in ips[ip])
                    {
                        sb.Append(port);
                        sb.Append('+');
                    }

                    sb.Append(',');
                }
                sb.Append(';');
            }

            if (endPointManagerServiceConnection == null)
            {
                SetRegisteredEndPoints(sb.ToString());
            }
            else
            {
                endPointManagerServiceConnection.SendAndReceiveEPM(sb.ToString());
            }
        }
        /// <summary>
        /// </summary>
        /// <param name="endPointManagerServiceConnection"></param>
        /// <returns></returns>
        private static Dictionary <string, Dictionary <string, HashSet <int> > > GetRegisteredEndPoints(EndPointManagerServiceConnection endPointManagerServiceConnection)
        {
            var endPoints = new Dictionary <string, Dictionary <string, HashSet <int> > >();

            string value = endPointManagerServiceConnection == null?GetRegisteredEndPoints() : endPointManagerServiceConnection.SendAndReceiveEPM(string.Empty);

            if (value.Length != 0)
            {
                string[] handleKvps = value.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
                foreach (string token1 in handleKvps)
                {
                    string[] handleKvp = token1.Split(new char[] { '*' }, StringSplitOptions.RemoveEmptyEntries);
                    string   handle    = handleKvp[0];

                    string[] ipKvps = handleKvp[1].Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                    foreach (string token2 in ipKvps)
                    {
                        string[] ipKvp = token2.Split(new char[] { '-' }, StringSplitOptions.RemoveEmptyEntries);
                        string   ip    = ipKvp[0];
                        var      ports = ipKvp[1].Split(new char[] { '+' }, StringSplitOptions.RemoveEmptyEntries);

                        if (!endPoints.ContainsKey(handle))
                        {
                            endPoints.Add(handle, new Dictionary <string, HashSet <int> >());
                        }
                        if (!endPoints[handle].ContainsKey(ip))
                        {
                            endPoints[handle].Add(ip, new HashSet <int>());
                        }

                        var hs = endPoints[handle][ip];
                        foreach (string port in ports)
                        {
                            hs.Add(int.Parse(port));
                        }
                    }
                }
            }

            //The service, if any, handles the cleaning. Otherwise this must be done here.
            if (endPointManagerServiceConnection == null)
            {
                CleanupEndPoints(endPoints, true);
            }

            return(endPoints);
        }