private void PingElapsed(object sender, NetMQTimerEventArgs e) { Assumes.NotNull(m_transmit); SendUdpFrame(m_transmit); }
private void Configure(string interfaceName, int port) { Assumes.NotNull(m_poller); Assumes.NotNull(m_pipe); // In case the beacon was configured twice if (m_udpSocket != null) { m_poller.Remove(m_udpSocket); #if NET35 m_udpSocket.Close(); #else m_udpSocket.Dispose(); #endif } m_udpPort = port; m_udpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); m_poller.Add(m_udpSocket, OnUdpReady); // Ask operating system for broadcast permissions on socket m_udpSocket.EnableBroadcast = true; // Allow multiple owners to bind to socket; incoming // messages will replicate to each owner m_udpSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); IPAddress?bindTo = null; IPAddress?sendTo = null; if (interfaceName == "*") { bindTo = IPAddress.Any; sendTo = IPAddress.Broadcast; } else if (interfaceName == "loopback") { bindTo = IPAddress.Loopback; sendTo = IPAddress.Broadcast; } else { var interfaceCollection = new InterfaceCollection(); var interfaceAddress = !string.IsNullOrEmpty(interfaceName) ? IPAddress.Parse(interfaceName) : null; foreach (var @interface in interfaceCollection) { if (interfaceAddress == null || @interface.Address.Equals(interfaceAddress)) { // because windows and unix differ in how they handle broadcast addressing this needs to be platform specific // on windows any interface can receive broadcast by requesting to enable broadcast on the socket // on linux to receive broadcast you must bind to the broadcast address specifically //bindTo = @interface.Address; sendTo = @interface.BroadcastAddress; #if NET45 || NET47_OR_GREATER if (Environment.OSVersion.Platform == PlatformID.Unix) #else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) #endif { bindTo = @interface.BroadcastAddress; } else { bindTo = @interface.Address; } sendTo = @interface.BroadcastAddress; break; } } } if (bindTo != null) { m_broadcastAddress = new IPEndPoint(sendTo, m_udpPort); m_udpSocket.Bind(new IPEndPoint(bindTo, m_udpPort)); } m_pipe.SendFrame(bindTo?.ToString() ?? ""); }
/// <summary> /// Runs the poller on the caller's thread. Only returns when <see cref="Stop"/> or <see cref="StopAsync"/> are called from another thread. /// </summary> private void RunPoller() { try { // Recalculate all timers now foreach (var timer in m_timers) { if (timer.Enable) { timer.When = Clock.NowMs() + timer.Interval; } } // Run until stop is requested while (!m_stopSignaler.IsStopRequested) { if (m_isPollSetDirty) { RebuildPollset(); } var pollStart = Clock.NowMs(); // Set tickless to "infinity" long tickless = pollStart + int.MaxValue; // Find the When-value of the earliest timer.. foreach (var timer in m_timers) { // If it is enabled but no When is set yet, if (timer.When == -1 && timer.Enable) { // Set this timer's When to now plus it's Interval. timer.When = pollStart + timer.Interval; } // If it has a When and that is earlier than the earliest found thus far, if (timer.When != -1 && tickless > timer.When) { // save that value. tickless = timer.When; } } // Compute a timeout value - how many milliseconds from now that the earliest-timer will expire. var timeout = tickless - pollStart; // Use zero to indicate it has already expired. if (timeout < 0) { timeout = 0; } var isItemAvailable = false; Assumes.NotNull(m_pollSet); if (m_pollSet.Length != 0) { isItemAvailable = m_netMqSelector.Select(m_pollSet, m_pollSet.Length, timeout); } else if (timeout > 0) { //TODO: Do we really want to simply sleep and return, doing nothing during this interval? //TODO: Should a large value be passed it will sleep for a month literally. // Solution should be different, but sleep is more natural here than in selector (timers are not selector concern). Debug.Assert(timeout <= int.MaxValue); Thread.Sleep((int)timeout); } // Get the expected end time in case we time out. This looks redundant but, unfortunately, // it happens that Poll takes slightly less than the requested time and 'Clock.NowMs() >= timer.When' // may not true, even if it is supposed to be. In other words, even when Poll times out, it happens // that 'Clock.NowMs() < pollStart + timeout' var expectedPollEnd = !isItemAvailable ? pollStart + timeout : -1L; // that way we make sure we can continue the loop if new timers are added. // timers cannot be removed foreach (var timer in m_timers) { if ((Clock.NowMs() >= timer.When || expectedPollEnd >= timer.When) && timer.When != -1) { timer.InvokeElapsed(this); if (timer.Enable) { timer.When = Clock.NowMs() + timer.Interval; } } } for (var i = 0; i < m_pollSet.Length; i++) { NetMQSelector.Item item = m_pollSet[i]; if (item.Socket != null) { Assumes.NotNull(m_pollact); NetMQSocket socket = m_pollact[i]; if (item.ResultEvent.HasError()) { if (++socket.Errors > 1) { Remove(socket); item.ResultEvent = PollEvents.None; } } else { socket.Errors = 0; } if (item.ResultEvent != PollEvents.None) { socket.InvokeEvents(this, item.ResultEvent); } } else if (item.ResultEvent.HasError() || item.ResultEvent.HasIn()) { Assumes.NotNull(item.FileDescriptor); if (m_pollinSockets.TryGetValue(item.FileDescriptor, out Action <Socket> action)) { action(item.FileDescriptor); } } } } #if !NET35 // Try to dequeue and execute all pending tasks before stopping poller while (m_tasksQueue.TryDequeue(out Task? task, TimeSpan.Zero)) { TryExecuteTask(task); } #endif } finally { foreach (var socket in m_sockets.ToList()) { Remove(socket); } } }