6using System.Net.NetworkInformation;
9using System.Threading.Tasks;
22 private const int ssdpPort = 1900;
23 private const int defaultMaximumSearchTimeSeconds = 10;
25 private readonly List<KeyValuePair<UdpClient, IPEndPoint>> ssdpOutgoing =
new List<KeyValuePair<UdpClient, IPEndPoint>>();
26 private readonly List<UdpClient> ssdpIncoming =
new List<UdpClient>();
27 private bool disposed =
false;
37 Dictionary<AddressFamily, bool> GenIncoming =
new Dictionary<AddressFamily, bool>();
41 foreach (NetworkInterface Interface
in NetworkInterface.GetAllNetworkInterfaces())
43 if (Interface.OperationalStatus != OperationalStatus.Up)
46 IPInterfaceProperties Properties = Interface.GetIPProperties();
47 IPAddress MulticastAddress;
49 foreach (UnicastIPAddressInformation UnicastAddress
in Properties.UnicastAddresses)
51 if (UnicastAddress.Address.AddressFamily == AddressFamily.InterNetwork && Socket.OSSupportsIPv4)
55 Outgoing =
new UdpClient(AddressFamily.InterNetwork);
56 MulticastAddress = IPAddress.Parse(
"239.255.255.250");
58 Outgoing.MulticastLoopback =
false;
65 else if (UnicastAddress.Address.AddressFamily == AddressFamily.InterNetworkV6 && Socket.OSSupportsIPv6)
69 Outgoing =
new UdpClient(AddressFamily.InterNetworkV6)
71 MulticastLoopback =
false
74 MulticastAddress = IPAddress.Parse(
"[FF02::C]");
84 Outgoing.EnableBroadcast =
true;
85 Outgoing.MulticastLoopback =
false;
87 Outgoing.Client.Bind(
new IPEndPoint(UnicastAddress.Address, 0));
90 Outgoing.JoinMulticastGroup(MulticastAddress);
92 this.
Warning(
"Address provided is not a multi-cast address.");
94 IPEndPoint EP =
new IPEndPoint(MulticastAddress, ssdpPort);
95 lock (this.ssdpOutgoing)
97 this.ssdpOutgoing.Add(
new KeyValuePair<UdpClient, IPEndPoint>(Outgoing, EP));
100 this.BeginReceiveOutgoing(Outgoing);
104 Incoming =
new UdpClient(Outgoing.Client.AddressFamily)
106 ExclusiveAddressUse =
false
109 Incoming.Client.Bind(
new IPEndPoint(UnicastAddress.Address, ssdpPort));
110 this.BeginReceiveIncoming(Incoming);
112 lock (this.ssdpIncoming)
114 this.ssdpIncoming.Add(Incoming);
122 if (!GenIncoming.ContainsKey(Outgoing.Client.AddressFamily))
124 GenIncoming[Outgoing.Client.AddressFamily] =
true;
128 Incoming =
new UdpClient(ssdpPort, Outgoing.Client.AddressFamily)
130 MulticastLoopback =
false
134 Incoming.JoinMulticastGroup(MulticastAddress);
136 this.
Warning(
"Address provided is not a multi-cast address.");
138 this.BeginReceiveIncoming(Incoming);
140 lock (this.ssdpIncoming)
142 this.ssdpIncoming.Add(Incoming);
154 private async
void BeginReceiveOutgoing(UdpClient Client)
158 while (!this.disposed)
160 UdpReceiveResult Data = await Client.ReceiveAsync();
164 byte[] Packet = Data.Buffer;
169 string Header = Encoding.ASCII.GetString(Packet);
178 if (!
string.IsNullOrEmpty(Headers.
Location))
188 await this.HandleIncoming(Client, Data.RemoteEndPoint, Headers);
192 await this.RaiseOnError(ex);
196 catch (ObjectDisposedException)
209 public event EventHandlerAsync<DeviceLocationEventArgs>
OnDeviceFound =
null;
211 private async Task HandleIncoming(UdpClient UdpClient, IPEndPoint RemoteIP,
UPnPHeaders Headers)
213 switch (Headers.
Verb)
233 public event EventHandlerAsync<NotificationEventArgs>
OnSearch =
null;
235 private async
void BeginReceiveIncoming(UdpClient Client)
239 while (!this.disposed)
241 UdpReceiveResult Data = await Client.ReceiveAsync();
245 byte[] Packet = Data.Buffer;
253 string Header = Encoding.ASCII.GetString(Packet);
258 if (!(Data.RemoteEndPoint is
null) &&
262 await this.HandleIncoming(Client, Data.RemoteEndPoint, Headers);
267 await this.RaiseOnError(ex);
271 catch (ObjectDisposedException)
286 return this.
StartSearch(
"upnp:rootdevice", defaultMaximumSearchTimeSeconds);
296 return this.
StartSearch(
"upnp:rootdevice", MaximumWaitTimeSeconds);
306 return this.
StartSearch(SearchTarget, defaultMaximumSearchTimeSeconds);
314 public async Task
StartSearch(
string SearchTarget,
int MaximumWaitTimeSeconds)
316 foreach (KeyValuePair<UdpClient, IPEndPoint> P
in this.GetOutgoing())
318 StringBuilder sb =
new StringBuilder();
320 sb.Append(
"M-SEARCH * HTTP/1.1\r\n");
322 sb.Append(P.Value.ToString());
323 sb.Append(
"\r\nMAN:\"ssdp:discover\"");
324 sb.Append(
"\r\nST: ");
325 sb.Append(SearchTarget);
326 sb.Append(
"\r\nMX:");
327 sb.Append(MaximumWaitTimeSeconds.ToString());
328 sb.Append(
"\r\n\r\n");
330 string MSearch = sb.ToString();
331 byte[] Packet = Encoding.ASCII.GetBytes(MSearch);
333 await this.SendPacket(P.Key, P.Value, Packet, MSearch);
337 private KeyValuePair<UdpClient, IPEndPoint>[] GetOutgoing()
339 return this.GetOutgoing(
false);
342 private KeyValuePair<UdpClient, IPEndPoint>[] GetOutgoing(
bool Clear)
344 lock (this.ssdpOutgoing)
346 KeyValuePair<UdpClient, IPEndPoint>[] Result = this.ssdpOutgoing.ToArray();
349 this.ssdpOutgoing.Clear();
355 private UdpClient[] GetIncoming()
357 return this.GetIncoming(
false);
360 private UdpClient[] GetIncoming(
bool Clear)
362 lock (this.ssdpIncoming)
364 UdpClient[] Result = this.ssdpIncoming.ToArray();
367 this.ssdpIncoming.Clear();
373 private async Task SendPacket(UdpClient Client, IPEndPoint Destination,
byte[] Packet,
string Text)
381 await Client.SendAsync(Packet, Packet.Length, Destination);
385 await this.RaiseOnError(ex);
389 private Task RaiseOnError(Exception ex)
391 return this.
OnError.Raise(
this, ex);
397 public event EventHandlerAsync<Exception>
OnError =
null;
402 [Obsolete(
"Use DisposeAsync() instead.")]
413 this.disposed =
true;
415 foreach (KeyValuePair<UdpClient, IPEndPoint> P
in this.GetOutgoing(
true))
427 foreach (UdpClient Client
in this.GetIncoming(
true))
444 await DisposableAsync.DisposeAsync();
445 else if (Sniffer is IDisposable Disposable)
446 Disposable.Dispose();
498 public async Task<DeviceDescriptionDocument>
GetDeviceAsync(
string Location,
int Timeout)
500 Uri LocationUri =
new Uri(Location);
501 using (HttpClient Client =
new HttpClient())
505 Client.Timeout = TimeSpan.FromMilliseconds(Timeout);
506 Stream Stream = await Client.GetStreamAsync(LocationUri);
508 XmlDocument Xml =
new XmlDocument()
510 PreserveWhitespace =
true
518 await this.RaiseOnError(ex);
569 using (HttpClient Client =
new HttpClient())
573 Client.Timeout = TimeSpan.FromMilliseconds(Timeout);
574 Stream Stream = await Client.GetStreamAsync(Service.
SCPDURI);
576 XmlDocument Xml =
new XmlDocument()
578 PreserveWhitespace =
true
586 await this.RaiseOnError(ex);
Static class managing the application event log. Applications and services log events on this static ...
static void Exception(Exception Exception, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, params KeyValuePair< string, object >[] Tags)
Logs an exception. Event type will be determined by the severity of the exception.
Simple base class for classes implementing communication protocols.
void TransmitText(string Text)
Called when text has been transmitted.
static bool IsMulticastAddress(IPAddress Address)
Checks if an IP Address is a multi-cast address or not.
void Exception(Exception Exception)
Called to inform the viewer of an exception state.
void ReceiveText(string Text)
Called when text has been received.
ISniffer[] Sniffers
Registered sniffers.
void Warning(string Warning)
Called to inform the viewer of a warning state.
void ReceiveBinary(int Count)
Called when binary data has been received.
Contains the information provided in a Device Description Document, downloaded from a device in the n...
Event arguments for completion events when downloading device description documents.
Contains information about the location of a device on the network.
Contains information about the location of a device on the network.
Contains the information provided in a Service Description Document, downloaded from a service in the...
Implements support for the UPnP protocol, as described in: http://upnp.org/specs/arch/UPnP-arch-Devic...
ServiceDescriptionDocument GetService(UPnPService Service, int Timeout)
Gets the service description document from a service in the network. This method is the synchronous v...
Task StartSearch(string SearchTarget)
Starts a search for devices on the network.
ServiceDescriptionDocument GetService(UPnPService Service)
Gets the service description document from a service in the network. This method is the synchronous v...
Task StartSearch(int MaximumWaitTimeSeconds)
Starts a search for devices on the network.
Task StartSearch()
Starts a search for devices on the network.
async Task StartSearch(string SearchTarget, int MaximumWaitTimeSeconds)
Starts a search for devices on the network.
EventHandlerAsync< DeviceLocationEventArgs > OnDeviceFound
Event raised when a device has been found as a result of a search made by the client.
async Task DisposeAsync()
IDisposableAsync.DisposeAsync
EventHandlerAsync< NotificationEventArgs > OnNotification
Event raised when the client is notified of a device or service in the network.
async Task< DeviceDescriptionDocument > GetDeviceAsync(string Location, int Timeout)
Gets a Device Description Document from a device.
Task< DeviceDescriptionDocument > GetDeviceAsync(string Location)
Gets a Device Description Document from a device.
async Task< ServiceDescriptionDocument > GetServiceAsync(UPnPService Service, int Timeout)
Gets a Service Description Document from a device.
Task< ServiceDescriptionDocument > GetServiceAsync(UPnPService Service)
Gets a Service Description Document from a device.
EventHandlerAsync< Exception > OnError
Event raised when an error occurs.
UPnPClient(params ISniffer[] Sniffers)
Implements support for the UPnP protocol, as described in: http://upnp.org/specs/arch/UPnP-arch-Devic...
EventHandlerAsync< NotificationEventArgs > OnSearch
Event raised when the client receives a request searching for devices or services in the network.
DeviceDescriptionDocument GetDevice(string Location)
Gets the device description document from a device in the network. This method is the synchronous ver...
void Dispose()
IDisposable.Dispose
DeviceDescriptionDocument GetDevice(string Location, int Timeout)
Gets the device description document from a device in the network. This method is the synchronous ver...
Contains information about a service.
Uri SCPDURI
URI to service description
Interface for asynchronously disposable objects.
Interface for sniffers. Sniffers can be added to ICommunicationLayer classes to eavesdrop on communic...
HttpDirection
Direction of message