/// <summary> /// Send an announce request for this InfoHash. /// </summary> /// <param name="infoHash"></param> /// <returns></returns> public Task Announce(InfoHash infoHash) { lock (PendingAnnounces) { PendingAnnounces.Enqueue(infoHash); if (!ProcessingAnnounces) { ProcessingAnnounces = true; ProcessQueue(); } } return(Task.CompletedTask); }
async void ProcessQueue() { // Ensure this doesn't run on the UI thread as the networking calls can do some (partially) blocking operations. // Specifically 'NetworkInterface.GetAllNetworkInterfaces' is synchronous and can take hundreds of milliseconds. await MainLoop.SwitchToThreadpool(); await RateLimiterTask; using var sendingClient = new UdpClient(); var nics = NetworkInterface.GetAllNetworkInterfaces(); while (true) { InfoHash infoHash = null; lock (PendingAnnounces) { if (PendingAnnounces.Count == 0) { // Enforce a minimum delay before the next announce to avoid killing CPU by iterating network interfaces. RateLimiterTask = Task.Delay(1000); ProcessingAnnounces = false; break; } infoHash = PendingAnnounces.Dequeue(); } string message = string.Format(BaseSearchString, ListenPort, infoHash.ToHex()); byte[] data = Encoding.ASCII.GetBytes(message); foreach (var nic in nics) { try { sendingClient.Client.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastInterface, IPAddress.HostToNetworkOrder(nic.GetIPProperties().GetIPv4Properties().Index)); await sendingClient.SendAsync(data, data.Length, MulticastAddressV4).ConfigureAwait(false); } catch { // If data can't be sent, just ignore the error } } } }