/// <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); }
/// <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); }