简体   繁体   中英

SSDP (UDP) on Windows Store applications (.NET)

I am trying to implement a basic SSDP ( UDP ) broadcast/listener for a Windows Store application using C# .

I have found that Windows.Networking.Sockets contains the DatagramSocket class which is what I need to use for UDP networking.

However, my current attempts seem to execute just fine but have no results via Wireshark and do not get a response back from the devices on the network.

Here is the code I am currently using (and running through the RT Simulator):

public async static Task<IEnumerable<HueBridge>> DiscoverAsync(TimeSpan timeout)
{
  if (timeout <= TimeSpan.Zero)
    throw new ArgumentException("Timeout value must be greater than zero.", "timeout");

  var discoveredBridges = new List<HueBridge>();

  using (var socket = new DatagramSocket())
  {
    while (true)
    {
      var bridgeWasFound = false;

      socket.MessageReceived += (sender, e) =>
      {
        var bpx = true; // breakpoint here for success
      };

      var multicastIP = new HostName("239.255.255.250");
      await socket.BindServiceNameAsync("1900");
      socket.JoinMulticastGroup(multicastIP);

      using (var writer = new DataWriter(socket.OutputStream))
      {
        var request = new StringBuilder();
        request.AppendLine("M-SEARCH * HTTP/1.1");
        request.AppendLine("HOST: 239.255.255.250:1900");
        request.AppendLine("MAN: ssdp:discover");
        request.AppendLine("MX: 5");
        request.AppendLine("ST: ssdp:all");

        writer.WriteString(request.ToString());
        await writer.FlushAsync();
      }

      if (timeout > TimeSpan.Zero)
        await Task.Delay(timeout);

      if (!bridgeWasFound)
        break; // breakpoint here for failure check
    }
  }

  return discoveredBridges;
}

Any ideas on what I may be doing incorrectly? I don't get an exception and I have the proper Capabilities set in the manifest. My breakpoint at the break always gets hit and I am using a timeout of 10 seconds.

Seems I have found the problem(s).

First, I should use socket.BindEndpointAsync(null, string.Empty) instead of socket.BindServiceNameAsync("1900") , which will properly listen for broadcast packets.

Secondly, writer.FlushAsync() does not write to the socket; however, writer.StoreAsync() does.

Here is the final result, which does work (almost) perfectly:

public async static Task<IEnumerable<HueBridge>> DiscoverAsync(TimeSpan timeout)
{
  if (timeout <= TimeSpan.Zero)
    throw new ArgumentException("Timeout value must be greater than zero.", "timeout");

  var discoveredBridges = new List<HueBridge>();
  var multicastIP = new HostName("239.255.255.250");
  var bridgeWasFound = false;

  using (var socket = new DatagramSocket())
  {
    socket.MessageReceived += (sender, e) =>
    {
      var reader = e.GetDataReader();
      var bytesRemaining = reader.UnconsumedBufferLength;
      var receivedString = reader.ReadString(bytesRemaining);

      // TODO: Check for existing bridges, only add new ones to prevent infinite loop.
      // TODO: Create new bridge and add to the list. 

      bridgeWasFound = true;
    };

    await socket.BindEndpointAsync(null, string.Empty);
    socket.JoinMulticastGroup(multicastIP);

    while (true)
    {
      bridgeWasFound = false;

      using (var stream = await socket.GetOutputStreamAsync(multicastIP, "1900"))
      using (var writer = new DataWriter(stream))
      {
        var request = new StringBuilder();
        request.AppendLine("M-SEARCH * HTTP/1.1");
        request.AppendLine("HOST: 239.255.255.250:1900");
        request.AppendLine("MAN: ssdp:discover");
        request.AppendLine("MX: 3");
        request.AppendLine("ST: ssdp:all");

        writer.WriteString(request.ToString());
        await writer.StoreAsync();

        if (timeout > TimeSpan.Zero)
          await Task.Delay(timeout);

        if (!bridgeWasFound)
          break;
      }
    }
  }

  return discoveredBridges;
}

According Specifications :

MAN REQUIRED by HTTP Extension Framework. Unlike the NTS and ST field values, the field value of the MAN header field is enclosed in double quotes; it defines the scope (namespace) of the extension. MUST be "ssdp:discover".

then your code

request.AppendLine("MAN: ssdp:discover");

must be

request.AppendLine("MAN: \"ssdp:discover\"");

Hope this help.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM