C# 使用多播套接字发现upnp设备的ssdp
我试图使用多播套接字在网络中发现UPnP设备,但是,我似乎多次获得相同的设备。这里的发现代码有什么问题 我得到的结果如下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
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)
{
//开始另一次阅读
插座