C# 使用多播套接字发现upnp设备的ssdp

C# 使用多播套接字发现upnp设备的ssdp,c#,upnp,multicastsocket,ssdp,C#,Upnp,Multicastsocket,Ssdp,我试图使用多播套接字在网络中发现UPnP设备,但是,我似乎多次获得相同的设备。这里的发现代码有什么问题 我得到的结果如下 namespace DevManager { using System; using System.Collections.Generic; using System.IO; using System.Net; using System.Net.Sockets; using System.Text; using Syst

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

我得到的结果如下

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.1\r\nHOST: {0}:{1}\r\nMAN: \"ssdp:discover\"\r\nMX: {2}\r\nST: {3}\r\n";

        /// <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;
        }
    }
}
HTTP/1.1200ok 高速缓存控制:最大年龄=60 提取: 地点: 服务器:网络打印机服务器UPnP/1.0 V4.00.01.31 2014年12月23日 ST:uuid:16a65700-007c-1000-bb49-30cda79cac19 USN:uuid:16a65700-007c-1000-bb49-30cda79cac19

HTTP/1.1200ok 高速缓存控制:最大年龄=60 提取: 地点: 服务器:网络打印机服务器UPnP/1.0 V4.00.01.31 2014年12月23日 ST:uuid:16a65700-007c-1000-bb49-30cda79b5419 USN: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.1\r\nHOST: {0}:{1}\r\nMAN: \"ssdp:discover\"\r\nMX: {2}\r\nST: {3}\r\n";

        /// <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;
        }
    }
}
名称空间DevManager
{
使用制度;
使用System.Collections.Generic;
使用System.IO;
Net系统;
使用System.Net.Sockets;
使用系统文本;
使用系统线程;
使用System.Threading.Tasks;
公共类UPnPDevDiscovery
{
/// 
///设备搜索请求
/// 
private const string searchRequest=“M-SEARCH*HTTP/1.1\r\n主机:{0}:{1}\r\n主机:\'ssdp:discover\”\r\nMX:{2}\r\nST:{3}\r\n”;
/// 
///广告多播地址
/// 
私有常量字符串multicastIP=“239.255.255.250”;
/// 
///广告多播端口
/// 
专用常数int多播端口=1900;
/// 
///多播消息的生存时间(TTL)
/// 
私有常量int多播TTL=4;
专用const int unicastPort=1901;
private const int MaxResultSize=8096;
私有常量字符串DefaultDeviceType=“ssdp:all”;
私有字符串设备类型;
反垄断基金的私人行动;
私有int搜索超时;
专用插座;
私人定时器;
私有int sendCount;
私有SocketAsyncEventArgs sendEvent;
私人厕所关闭;
私有列表taskList=新列表();
public void Initialize(字符串设备类型、int searchTimeOut、操作onDeviceFound)
{
如果(searchTimeOut<1 | | searchTimeOut>4)
{
this.searchTimeOut=多播TTL;
}
其他的
{
this.searchTimeOut=searchTimeOut;
}
if(string.IsNullOrWhiteSpace(deviceType))
{
this.deviceType=DefaultDeviceType;
}
其他的
{
this.deviceType=设备类型;
}
this.onDeviceFound=onDeviceFound;
}
公共设施
{
string request=string.Format(searchRequest、multicastIP、multicastPort、this.searchTimeOut、this.deviceType);
套接字=新套接字(AddressFamily.InterNetwork、SocketType.Dgram、ProtocolType.Udp);
byte[]multiCastData=Encoding.UTF8.GetBytes(请求);
socket.SendBufferSize=多播数据.Length;
sendEvent=new SocketAsyncEventArgs();
sendEvent.RemoteEndPoint=新的IPEndPoint(IPAddress.Parse(multicastIP),multicastPort);
SetBuffer(multiCastData,0,multiCastData.Length);
sendEvent.Completed+=OnSocketSendEventCompleted;
//为搜索时间加上一秒设置一次计时器
TimerCallback cb=新TimerCallback((状态)=>
{
this.socketClosed=true;
socket.Close();
});
计时器=新计时器(cb,null,TimeSpan.FromSeconds(this.searchTimeOut+1),new TimeSpan(-1));
//启动初始发送
this.sendCount=3;
this.socketClosed=false;
socket.SendToAsync(sendEvent);
//而(!this.socketClosed)
//{
//睡眠(200);
//}
//Task.WaitAll(this.taskList.ToArray());
//这个.taskList.Clear();
}
SocketSendEventCompleted上的私有void(对象发送方,SocketAsyncEventArgs e)
{
如果(例如SocketError!=SocketError.Success)
{
此.AddDevice(空);
}
其他的
{
if(e.LastOperation==SocketAsyncOperation.SendTo)
{
如果(--this.sendCount!=0)
{
如果(!this.socketClosed)
{
socket.SendToAsync(sendEvent);
}
}
其他的
{
//初始多播完成后,准备接收响应
e、 RemoteEndPoint=新的IPEndPoint(IPAddress.Any,0);
socket.ReceiveBufferSize=MaxResultSize;
字节[]接收缓冲区=新字节[MaxResultSize];
e、 SetBuffer(receiveBuffer,0,MaxResultSize);
socket.receivefromsync(e);
}
}
else if(e.LastOperation==SocketAsyncOperation.ReceiveFrom)
{
//收到回应,所以解码
字符串结果=Encoding.UTF8.GetString(e.Buffer,0,e.ByTestTransferred);
if(result.StartsWith(“HTTP/1.1 200 OK”,StringComparison.InvariantCultureIgnoreCase))
{
//解析设备并调用回调
添加设备(结果);
}
其他的
{
//Debug.WriteLine(“无效搜索响应”);
}
如果(!this.socketClosed)
{
//开始另一次阅读
插座