使用多播套接字的 UPnP 设备的 SSDP 发现



我正在尝试使用组播套接字发现网络中的UPnP设备,但是,我似乎多次获得相同的设备。这里的发现代码有什么问题。

我得到的结果如下

HTTP/1.1 200 OK缓存控制:最大年龄 = 60内线:位置: http://10.2.1.89:5200/Printer.xml服务器: 网络打印机服务器 UPnP/1.0 V4.00.01.31 2014-12-23ST: uuid:16a65700-007c-1000-bb49-30cda79cac19USN: uuid:16a65700-007c-1000-bb49-30cda79cac19

HTTP/1.1 200 OK缓存控制:最大年龄 = 60内线:地点: http://10.2.1.87:5200/Printer.xml服务器: 网络打印机服务器 UPnP/1.0 V4.00.01.31 2014-12-23ST: uuid:16a65700-007c-1000-bb49-30cda79b5419美国海军: uuid:16a65700-007c-1000-bb49-30cda79b5419

使用的代码如下

namespace DevManager
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Net;
    using System.Net.Sockets;
    using System.Text;
    using System.Threading;
    using System.Threading.Tasks;
    public class UPnPDevDiscovery 
    {
        /// <summary>
        /// Device search request
        /// </summary>
        private const string searchRequest = "M-SEARCH * HTTP/1.1rnHOST: {0}:{1}rnMAN: "ssdp:discover"rnMX: {2}rnST: {3}rn";
        /// <summary>
        /// Advertisement multicast address
        /// </summary>
        private const string multicastIP = "239.255.255.250";
        /// <summary>
        /// Advertisement multicast port
        /// </summary>
        private const int multicastPort = 1900;
        /// <summary>
        ///  Time to Live (TTL) for multicast messages
        /// </summary>
        private const int multicastTTL = 4;
        private const int unicastPort = 1901;
        private const int MaxResultSize = 8096;
        private const string DefaultDeviceType = "ssdp:all";
        private string deviceType;
        private Action<Device> onDeviceFound;
        private int searchTimeOut;
        private Socket socket;
        private Timer timer;
        private int sendCount;
        private SocketAsyncEventArgs sendEvent;
        private bool socketClosed;
        private List<Task> taskList = new List<Task>();
        public void Initialize(string deviceType, int searchTimeOut, Action<Device> onDeviceFound)
        {
            if (searchTimeOut < 1 || searchTimeOut > 4)
            {
                this.searchTimeOut = multicastTTL;
            }
            else
            {
                this.searchTimeOut = searchTimeOut;
            }
            if (string.IsNullOrWhiteSpace(deviceType))
            {
                this.deviceType = DefaultDeviceType;
            }
            else
            {
                this.deviceType = deviceType;
            }
            this.onDeviceFound = onDeviceFound;
        }
        public void FindDevices()
        {
            string request = string.Format(searchRequest, multicastIP, multicastPort, this.searchTimeOut, this.deviceType);
            socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            byte[] multiCastData = Encoding.UTF8.GetBytes(request);
            socket.SendBufferSize = multiCastData.Length;
            sendEvent = new SocketAsyncEventArgs();
            sendEvent.RemoteEndPoint = new IPEndPoint(IPAddress.Parse(multicastIP), multicastPort);
            sendEvent.SetBuffer(multiCastData, 0, multiCastData.Length);
            sendEvent.Completed += OnSocketSendEventCompleted;
            // Set a one-shot timer for the Search time plus a second
            TimerCallback cb = new TimerCallback((state) =>
            {
                this.socketClosed = true;
                socket.Close();
            });
            timer = new Timer(cb, null, TimeSpan.FromSeconds(this.searchTimeOut + 1), new TimeSpan(-1));
            // Kick off the initial Send
            this.sendCount = 3;
            this.socketClosed = false;
            socket.SendToAsync(sendEvent);
            //while (!this.socketClosed)
            //{
            //    Thread.Sleep(200);
            //}
            //Task.WaitAll(this.taskList.ToArray());
            //this.taskList.Clear();
        }
        private void OnSocketSendEventCompleted(object sender, SocketAsyncEventArgs e)
        {
            if (e.SocketError != SocketError.Success)
            {
                this.AddDevice(null);
            }
            else
            {
                if (e.LastOperation == SocketAsyncOperation.SendTo)
                {
                    if (--this.sendCount != 0)
                    {
                        if (!this.socketClosed)
                        {
                            socket.SendToAsync(sendEvent);
                        }
                    }
                    else
                    {
                        // When the initial multicast is done, get ready to receive responses
                        e.RemoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
                        socket.ReceiveBufferSize = MaxResultSize;
                        byte[] receiveBuffer = new byte[MaxResultSize];
                        e.SetBuffer(receiveBuffer, 0, MaxResultSize);
                        socket.ReceiveFromAsync(e);
                    }
                }
                else if (e.LastOperation == SocketAsyncOperation.ReceiveFrom)
                {
                    // Got a response, so decode it
                    string result = Encoding.UTF8.GetString(e.Buffer, 0, e.BytesTransferred);
                    if (result.StartsWith("HTTP/1.1 200 OK", StringComparison.InvariantCultureIgnoreCase))
                    {
                        //parse device and invoke callback
                        AddDevice(result);
                    }
                    else
                    {
                        //Debug.WriteLine("INVALID SEARCH RESPONSE");
                    }
                    if (!this.socketClosed)
                    {
                        // and kick off another read
                        socket.ReceiveFromAsync(e);
                    }
                    else
                    {
                        // unless socket was closed, when declare the scan is complete
                        //AddDevice(result);
                    }
                }
            }
        }
        private void AddDevice(string response)
        {
            Console.WriteLine(response);
            //Task addDeviceTask = Task.Run(() =>
            //{
            //    // parse the result and download the device description
            //    if (this.onDeviceFound != null && response != null)
            //    {
            //        Dictionary<string, string> ssdpResponse = ParseSSDPResponse(response);
            //        HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(ssdpResponse["location"]);
            //        WebResponse webResponse = webRequest.GetResponse();
            //        using (DeviceXml deviceXml = new DeviceXml(webResponse.GetResponseStream()))
            //        {
            //            this.onDeviceFound(deviceXml.GetObject());
            //        }
            //    }
            //});
            //this.taskList.Add(addDeviceTask);
        }
        // Probably not exactly compliant with RFC 2616 but good enough for now
        private Dictionary<string, string> ParseSSDPResponse(string response)
        {
            StringReader reader = new StringReader(response);
            string line = reader.ReadLine();
            if (line != "HTTP/1.1 200 OK")
                return null;
            Dictionary<string, string> result = new Dictionary<string, string>();
            for (;;)
            {
                line = reader.ReadLine();
                if (line == null)
                    break;
                if (line != "")
                {
                    int colon = line.IndexOf(':');
                    if (colon < 1)
                    {
                        return null;
                    }
                    string name = line.Substring(0, colon).Trim();
                    string value = line.Substring(colon + 1).Trim();
                    if (string.IsNullOrEmpty(name))
                    {
                        return null;
                    }
                    result[name.ToLowerInvariant()] = value;
                }
            }
            return result;
        }
    }
}
我已经将

上述内容移到了这里的最小工作实现中:

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Timers;
namespace SomeApp
{
    public class UPnPDevDiscovery
    {
        /// <summary>
        /// Device search request
        /// </summary>
    private const string searchRequest = @"M-SEARCH * HTTP/1.1
HOST: {0}:{1}
MAN: ""ssdp:discover""
MX: {2}
ST: {3}
";
        /// <summary>
        /// Advertisement multicast address
        /// </summary>
        private const string MulticastIP = "239.255.255.250";
        /// <summary>
        /// Advertisement multicast port
        /// </summary>
        private const int multicastPort = 1900;
        /// <summary>
        ///  Time to Live (TTL) for multicast messages
        /// </summary>
        private const int multicastTTL = 4;
        private const int MaxResultSize = 8096;
        private const string DefaultDeviceType = "ssdp:all";
        private int searchTimeOut = 5; //Seconds
        private Socket socket;
        private SocketAsyncEventArgs sendEvent;
        public void FindDevices()
        {
            string request = string.Format(searchRequest, MulticastIP, multicastPort, this.searchTimeOut, DefaultDeviceType);
            Console.WriteLine("Sending: n" + request);
            byte[] multiCastData = Encoding.UTF8.GetBytes(request);
            socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            socket.SendBufferSize = multiCastData.Length;
            sendEvent = new SocketAsyncEventArgs();
            sendEvent.RemoteEndPoint = new IPEndPoint(IPAddress.Parse(MulticastIP), multicastPort);
            sendEvent.SetBuffer(multiCastData, 0, multiCastData.Length);
            sendEvent.Completed += OnSocketSendEventCompleted;
            Timer t = new Timer(TimeSpan.FromSeconds(this.searchTimeOut + 1).TotalMilliseconds);
            t.Elapsed += (e, s) => {
                socket.Dispose();
                socket = null;
            };
            // Kick off the initial Send
            socket.SetSocketOption(SocketOptionLevel.IP,SocketOptionName.MulticastInterface, IPAddress.Parse(MulticastIP).GetAddressBytes());
            socket.SendToAsync(sendEvent);
            t.Start();
        }
        private void OnSocketSendEventCompleted(object sender, SocketAsyncEventArgs e)
        {
            if (e.SocketError != SocketError.Success)
            {
                Console.WriteLine("SocketError: " + e.SocketError);
                return;
            }
            switch (e.LastOperation)
            {
                case SocketAsyncOperation.SendTo:
                    Console.WriteLine("Send complete");
                    // When the initial multicast is done, get ready to receive responses
                    e.RemoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
                    byte[] receiveBuffer = new byte[MaxResultSize];
                    socket.ReceiveBufferSize = receiveBuffer.Length;
                    e.SetBuffer(receiveBuffer, 0, MaxResultSize);
                    Console.WriteLine("Waiting for response");
                    socket.ReceiveFromAsync(e);
                    break;
                case SocketAsyncOperation.ReceiveFrom:
                    Console.WriteLine("Received:");
                    // Got a response, so decode it
                    string result = Encoding.UTF8.GetString(e.Buffer, 0, e.BytesTransferred);
                    if (result.StartsWith("HTTP/1.1 200 OK", StringComparison.InvariantCultureIgnoreCase))
                        Console.WriteLine(result);
                    else
                        Console.WriteLine("INVALID SEARCH RESPONSEn" + result);
                    if (socket != null)// and kick off another read
                        socket.ReceiveFromAsync(e);                    
                    break;
                default:
                    Console.WriteLine("***"+e.LastOperation);
                    break;
            }
        }
    }
}

相关内容

  • 没有找到相关文章