2using System.Collections.Generic;
6using System.Net.NetworkInformation;
7using System.Net.Sockets;
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));
88 Outgoing.JoinMulticastGroup(MulticastAddress);
90 IPEndPoint EP =
new IPEndPoint(MulticastAddress, ssdpPort);
91 lock (this.ssdpOutgoing)
93 this.ssdpOutgoing.Add(
new KeyValuePair<UdpClient, IPEndPoint>(Outgoing, EP));
96 this.BeginReceiveOutgoing(Outgoing);
100 Incoming =
new UdpClient(Outgoing.Client.AddressFamily)
102 ExclusiveAddressUse =
false
105 Incoming.Client.Bind(
new IPEndPoint(UnicastAddress.Address, ssdpPort));
106 this.BeginReceiveIncoming(Incoming);
108 lock (this.ssdpIncoming)
110 this.ssdpIncoming.Add(Incoming);
118 if (!GenIncoming.ContainsKey(Outgoing.Client.AddressFamily))
120 GenIncoming[Outgoing.Client.AddressFamily] =
true;
124 Incoming =
new UdpClient(ssdpPort, Outgoing.Client.AddressFamily)
126 MulticastLoopback =
false
129 Incoming.JoinMulticastGroup(MulticastAddress);
130 this.BeginReceiveIncoming(Incoming);
132 lock (this.ssdpIncoming)
134 this.ssdpIncoming.Add(Incoming);
146 private async
void BeginReceiveOutgoing(UdpClient Client)
150 while (!this.disposed)
152 UdpReceiveResult Data = await Client.ReceiveAsync();
156 byte[] Packet = Data.Buffer;
161 string Header = Encoding.ASCII.GetString(Packet);
170 if (!
string.IsNullOrEmpty(Headers.
Location))
180 await this.HandleIncoming(Client, Data.RemoteEndPoint, Headers);
184 await this.RaiseOnError(ex);
188 catch (ObjectDisposedException)
201 public event EventHandlerAsync<DeviceLocationEventArgs>
OnDeviceFound =
null;
203 private async Task HandleIncoming(UdpClient UdpClient, IPEndPoint RemoteIP,
UPnPHeaders Headers)
205 switch (Headers.
Verb)
225 public event EventHandlerAsync<NotificationEventArgs>
OnSearch =
null;
227 private async
void BeginReceiveIncoming(UdpClient Client)
231 while (!this.disposed)
233 UdpReceiveResult Data = await Client.ReceiveAsync();
237 byte[] Packet = Data.Buffer;
245 string Header = Encoding.ASCII.GetString(Packet);
250 if (!(Data.RemoteEndPoint is
null) &&
254 await this.HandleIncoming(Client, Data.RemoteEndPoint, Headers);
259 await this.RaiseOnError(ex);
263 catch (ObjectDisposedException)
278 return this.
StartSearch(
"upnp:rootdevice", defaultMaximumSearchTimeSeconds);
288 return this.
StartSearch(
"upnp:rootdevice", MaximumWaitTimeSeconds);
298 return this.
StartSearch(SearchTarget, defaultMaximumSearchTimeSeconds);
306 public async Task
StartSearch(
string SearchTarget,
int MaximumWaitTimeSeconds)
308 foreach (KeyValuePair<UdpClient, IPEndPoint> P
in this.GetOutgoing())
310 string MSearch =
"M-SEARCH * HTTP/1.1\r\n" +
311 "HOST: " + P.Value.ToString() +
"\r\n" +
312 "MAN:\"ssdp:discover\"\r\n" +
313 "ST: " + SearchTarget +
"\r\n" +
314 "MX:" + MaximumWaitTimeSeconds.ToString() +
"\r\n\r\n";
315 byte[] Packet = Encoding.ASCII.GetBytes(MSearch);
317 await this.SendPacket(P.Key, P.Value, Packet, MSearch);
321 private KeyValuePair<UdpClient, IPEndPoint>[] GetOutgoing()
323 return this.GetOutgoing(
false);
326 private KeyValuePair<UdpClient, IPEndPoint>[] GetOutgoing(
bool Clear)
328 lock (this.ssdpOutgoing)
330 KeyValuePair<UdpClient, IPEndPoint>[] Result = this.ssdpOutgoing.ToArray();
333 this.ssdpOutgoing.Clear();
339 private UdpClient[] GetIncoming()
341 return this.GetIncoming(
false);
344 private UdpClient[] GetIncoming(
bool Clear)
346 lock (this.ssdpIncoming)
348 UdpClient[] Result = this.ssdpIncoming.ToArray();
351 this.ssdpIncoming.Clear();
357 private async Task SendPacket(UdpClient Client, IPEndPoint Destination,
byte[] Packet,
string Text)
365 await Client.SendAsync(Packet, Packet.Length, Destination);
369 await this.RaiseOnError(ex);
373 private Task RaiseOnError(Exception ex)
375 return this.
OnError.Raise(
this, ex);
381 public event EventHandlerAsync<Exception>
OnError =
null;
388 this.disposed =
true;
390 foreach (KeyValuePair<UdpClient, IPEndPoint> P
in this.GetOutgoing(
true))
402 foreach (UdpClient Client
in this.GetIncoming(
true))
416 if (Sniffer is IDisposable Disposable)
420 Disposable.Dispose();
473 public async Task<DeviceDescriptionDocument>
GetDeviceAsync(
string Location,
int Timeout)
475 Uri LocationUri =
new Uri(Location);
476 using (HttpClient Client =
new HttpClient())
480 Client.Timeout = TimeSpan.FromMilliseconds(Timeout);
481 Stream Stream = await Client.GetStreamAsync(LocationUri);
483 XmlDocument Xml =
new XmlDocument()
485 PreserveWhitespace =
true
493 await this.RaiseOnError(ex);
544 using (HttpClient Client =
new HttpClient())
548 Client.Timeout = TimeSpan.FromMilliseconds(Timeout);
549 Stream Stream = await Client.GetStreamAsync(Service.
SCPDURI);
551 XmlDocument Xml =
new XmlDocument()
553 PreserveWhitespace =
true
561 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.
Task Exception(Exception Exception)
Called to inform the viewer of an exception state.
ISniffer[] Sniffers
Registered sniffers.
Task TransmitText(string Text)
Called when text has been transmitted.
Task ReceiveText(string Text)
Called when text has been received.
Task ReceiveBinary(byte[] Data)
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.
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 sniffers. Sniffers can be added to ICommunicationLayer classes to eavesdrop on communic...
HttpDirection
Direction of message