Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Announce a service #82

Merged
merged 6 commits into from
Oct 17, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion src/MulticastService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ public MulticastService(Func<IEnumerable<NetworkInterface>, IEnumerable<NetworkI

UseIpv4 = Socket.OSSupportsIPv4;
UseIpv6 = Socket.OSSupportsIPv6;
IgnoreDuplicateMessages = true;
}

/// <summary>
Expand All @@ -150,6 +151,18 @@ public MulticastService(Func<IEnumerable<NetworkInterface>, IEnumerable<NetworkI
/// </value>
public bool UseIpv6 { get; set; }

/// <summary>
/// Determines if received messages are checked for duplicates.
/// </summary>
/// <value>
/// <b>true</b> to ignore duplicate messages. Defaults to <b>true</b>.
/// </value>
/// <remarks>
/// When set, a message that has been received within the last minute
/// will be ignored.
/// </remarks>
public bool IgnoreDuplicateMessages { get; set; }

/// <summary>
/// The interval for discovering network interfaces.
/// </summary>
Expand Down Expand Up @@ -619,7 +632,7 @@ void Send(Message msg, bool checkDuplicate, IPEndPoint remoteEndPoint = null)
public void OnDnsMessage(object sender, UdpReceiveResult result)
{
// If recently received, then ignore.
if (!receivedMessages.TryAdd(result.Buffer))
if (IgnoreDuplicateMessages && !receivedMessages.TryAdd(result.Buffer))
{
return;
}
Expand Down
35 changes: 35 additions & 0 deletions src/ServiceDiscovery.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Common.Logging;
using Makaretu.Dns.Resolving;

Expand Down Expand Up @@ -256,6 +257,40 @@ public void Advertise(ServiceProfile service)
catalog.IncludeReverseLookupRecords();
}

/// <summary>
/// Sends an unsolicited MDNS response describing the
/// service profile.
/// </summary>
/// <param name="profile">
/// The profile to describe.
/// </param>
/// <remarks>
/// Sends a MDNS response <see cref="Message"/> containing the pointer
/// and resource records of the <paramref name="profile"/>.
/// <para>
/// To provide increased robustness against packet loss,
/// two unsolicited responses are sent one second apart.
/// </para>
/// </remarks>
public void Announce(ServiceProfile profile)
{
var message = new Message { QR = true };

// Add the shared records.
var ptrRecord = new PTRRecord { Name = profile.QualifiedServiceName, DomainName = profile.FullyQualifiedName };
message.Answers.Add(ptrRecord);

// Add the resource records.
profile.Resources.ForEach((resource) =>
{
message.Answers.Add(resource);
});

Mdns.SendAnswer(message, checkDuplicate: false);
Task.Delay(1000).Wait();
Mdns.SendAnswer(message, checkDuplicate: false);
}

/// <summary>
/// Sends a goodbye message for the provided
/// profile and removes its pointer from the name sever.
Expand Down
1 change: 1 addition & 0 deletions test/MulticastServiceTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public void Can_Create()
{
var mdns = new MulticastService();
Assert.IsNotNull(mdns);
Assert.IsTrue(mdns.IgnoreDuplicateMessages);
}

[TestMethod]
Expand Down
99 changes: 99 additions & 0 deletions test/ServiceDiscoveryTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -454,5 +454,104 @@ public void ResourceRecords()
}
}

[TestMethod]
public void Announce_ContainsSharedRecords()
{
var service = new ServiceProfile("z", "_sdtest-4._udp", 1024, new[] { IPAddress.Loopback });
var done = new ManualResetEvent(false);

var mdns = new MulticastService();
mdns.AnswerReceived += (s, e) =>
{
var msg = e.Message;
if (msg.Answers.OfType<PTRRecord>().Any(p => p.DomainName == service.FullyQualifiedName))
{
done.Set();
}
};
try
{
using (var sd = new ServiceDiscovery(mdns))
{
mdns.NetworkInterfaceDiscovered += (s, e) => sd.Announce(service);
mdns.Start();
Assert.IsTrue(done.WaitOne(TimeSpan.FromSeconds(3)), "announce timeout");
}
}
finally
{
mdns.Stop();
}
}

[TestMethod]
public void Announce_ContainsResourceRecords()
{
var service = new ServiceProfile("z", "_sdtest-4._udp", 1024, new[] { IPAddress.Loopback });
var done = new ManualResetEvent(false);

var mdns = new MulticastService();
mdns.AnswerReceived += (s, e) =>
{
var msg = e.Message;
foreach (var r in service.Resources)
{
if (!msg.Answers.Contains(r))
{
return;
}
}
done.Set();
};
try
{
using (var sd = new ServiceDiscovery(mdns))
{
mdns.NetworkInterfaceDiscovered += (s, e) => sd.Announce(service);
mdns.Start();
Assert.IsTrue(done.WaitOne(TimeSpan.FromSeconds(3)), "announce timeout");
}
}
finally
{
mdns.Stop();
}
}

[TestMethod]
public void Announce_SentTwice()
{
var service = new ServiceProfile("z", "_sdtest-4._udp", 1024, new[] { IPAddress.Loopback });
var done = new ManualResetEvent(false);
var nanswers = 0;
var mdns = new MulticastService
{
IgnoreDuplicateMessages = false
};
mdns.AnswerReceived += (s, e) =>
{
var msg = e.Message;
if (msg.Answers.OfType<PTRRecord>().Any(p => p.DomainName == service.FullyQualifiedName))
{
if (++nanswers == 2)
{
done.Set();
}
}
};
try
{
using (var sd = new ServiceDiscovery(mdns))
{
mdns.NetworkInterfaceDiscovered += (s, e) => sd.Announce(service);
mdns.Start();
Assert.IsTrue(done.WaitOne(TimeSpan.FromSeconds(4)), "announce timeout");
}
}
finally
{
mdns.Stop();
}
}
}
}