private DpwsServiceDescriptions ProcessMatch(DpwsServiceDescription.ServiceDescriptionType type, byte[] response, string messageID, IPEndPoint remoteEP, WsMessageCheck messageCheck) { Microsoft.SPOT.Debug.Assert(type == DpwsServiceDescription.ServiceDescriptionType.ProbeMatch || type == DpwsServiceDescription.ServiceDescriptionType.ResolveMatch); // Parse and build header WsWsaHeader header; XmlReader reader; try { reader = WsSoapMessageParser.ParseSoapMessage(response, out header); } catch { return null; } try { // Make sure this is a probe matches response String headerAction = (type == DpwsServiceDescription.ServiceDescriptionType.ProbeMatch) ? WsWellKnownUri.WsdNamespaceUri + "/ProbeMatches" : WsWellKnownUri.WsdNamespaceUri + "/ResolveMatches"; if (header.Action != headerAction) return null; // Make sure this is not a duplicate probe response if (messageCheck != null) { if (messageCheck.IsDuplicate(header.MessageID, remoteEP.ToString()) == true) { System.Ext.Console.Write("ProbeMatches / ResolveMatches - Duplicate response - " + header.Action + " received"); return null; } } // Make sure the messageID matches the request ID if (header.RelatesTo != messageID) return null; // Process the probe matches #if DEBUG int depth = reader.Depth; #endif DpwsServiceDescriptions matches = new DpwsServiceDescriptions(reader, type); #if DEBUG Microsoft.SPOT.Debug.Assert(XmlReaderHelper.HasReadCompleteNode(depth, reader)); #endif return matches; } finally { reader.Close(); } }
//--// public DpwsDiscoClientService(DpwsClient client, ProtocolVersion version) { m_client = client; m_threadLock = new object(); m_version = version; m_messageCheck = new WsMessageCheck(); // Add discovery Hello ServiceOperations m_discoCallbacks = new WsServiceOperations(); m_discoCallbacks.Add(new WsServiceOperation(m_version.DiscoveryNamespace, "Hello")); m_discoCallbacks.Add(new WsServiceOperation(m_version.DiscoveryNamespace, "Bye")); }
/// <summary> /// Parses a ResolveMatches message. /// </summary> /// <param name="resolveResponse">A byte array containing a resolve response soap message.</param> /// <param name="messageID"> /// A string containing the message ID of the original request. This is used to make insure this is /// a response to the original request. /// </param> /// <param name="remoteEP"> /// A IPEndPoint containing the remote address of the service that sent this response. /// </param> /// <param name="messageCheck"> /// A WsdMessageCheck object ued to test for a duplicate request message. /// If null no check is performed as would be the case for a directed probe response. /// </param> /// <returns> /// A DpwsResolveMatch object containing a service endpoint description returned by a service. /// </returns> /// <exception cref="XmlException">If required tags are missing or invalid namespaces are detected.</exception> public DpwsServiceDescription ProcessResolveMatch(WsMessage resolveResponse, string messageID, IPEndPoint remoteEP, WsMessageCheck messageCheck) { DpwsServiceDescriptions resolveMatches = ProcessMatch(DpwsServiceDescription.ServiceDescriptionType.ResolveMatch, resolveResponse, messageID, remoteEP, messageCheck); return (resolveMatches == null ? null : resolveMatches[0]); }
/// <summary> /// Parses a ProbeMatches message. /// </summary> /// <param name="probeResponse">A byte array containing a probe response soap message.</param> /// <param name="messageID"> /// A string containing the message ID of the original request. This is used to make insure this is /// a response to the original request. /// </param> /// <param name="remoteEP"> /// A IPEndPoint containing the remote address of the service that sent this response. /// </param> /// <param name="messageCheck"> /// A WsMessageCheck object ued to test for a duplicate request message. /// If null no check is performed as would be the case for a directed probe response. /// </param> /// <returns> /// A DpwsProbeMatch object containing a service endpoint description returned by a service. /// </returns> /// <exception cref="XmlException">If required tags are missing or invalid namespaces are detected.</exception> public DpwsServiceDescriptions ProcessProbeMatch(WsMessage probeResponse, string messageID, IPEndPoint remoteEP, WsMessageCheck messageCheck) { return ProcessMatch(DpwsServiceDescription.ServiceDescriptionType.ProbeMatch, probeResponse, messageID, remoteEP, messageCheck); }
/// <summary> /// Listens for Udp request on 239.255.255.250:3702 /// </summary> /// <remarks>On initialization it sends a Discovery Hello message and listens on the Ws-Discovery /// endpoint for a request. When a request arrives it starts a UdpProcess thread that processes the message. /// The number of UdpProcessing threads are limited by the Device.MaxUdpRequestThreads property. /// </remarks> private void Listen() { // Create a duplicate message tester. WsMessageCheck messageCheck = new WsMessageCheck(40); while (!m_requestStop) { try { // If threads ara availble receive next message. If we are waiting on threads let the socket // buffer request until we get a thread. This will work until the reveice buffer is depleted // at which time request will be dropped if (m_threadManager.ThreadsAvailable == true) { RequestContext req = m_replyChannel.ReceiveRequest(); if (req != null) { WsWsaHeader header = req.Message.Header; if (header.MessageID != null && messageCheck.IsDuplicate(header.MessageID,header.From != null ? header.From.Address.AbsoluteUri : "")) { continue; } // Try to get a processing thread and process the request m_threadManager.StartNewThread(new WsUdpMessageProcessor(m_serviceEndpoints, req)); } else { System.Ext.Console.Write("UDP Receive returned 0 bytes"); } } else { System.Ext.Console.Write("Udp service host waiting for a thread..."); m_threadManager.ThreadEvent.WaitOne(); } } catch (SocketException se) { // Since the MF Socket does not have IOControl that would be used to turn off ICMP notifications // for UDP, catch 10054 and try to continue if ((SocketError)se.ErrorCode == SocketError.ConnectionReset) { Thread.Sleep(100); } } catch (Exception e) { System.Ext.Console.Write(e.Message + " " + e.InnerException); } } }
/// <summary> /// Use to send a resolve request to the ws-discovery address and receive a resolve match. /// </summary> /// <param name="message">A byte array containing a the resolve message.</param> /// <param name="messageID"> /// A string containing the message ID of a resolve request. This ID will be used to validate against /// a ResolveMatch received if it don't match, the ResolveMatch is discarded. /// </param> /// <param name="timeout"> /// A DateTime value containing the length of time this request will wait for resolve match. /// until the timeout value has expired. /// </param> /// <returns>A resolve match object.</returns> private DpwsServiceDescription SendResolveRequest(byte[] message, string messageID, long timeout) { WsMessageCheck messageCheck = new WsMessageCheck(); DpwsServiceDescription resolveMatch = null; System.Ext.Console.Write(""); System.Ext.Console.Write("Sending Resolve:"); System.Ext.Console.Write(message); // Create a new UdpClient using(Socket udpClient = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)) { IPEndPoint localEP = new IPEndPoint(IPAddress.Parse(WsNetworkServices.GetLocalIPV4Address()), m_discoResponsePort); udpClient.Bind(localEP); // Very important - Set default multicast interface for the underlying socket udpClient.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastInterface, (int)WsNetworkServices.GetLocalIPV4AddressValue()); udpClient.ReceiveTimeout = m_receiveTimeout; // Random back off implemented as per soap over udp specification // for unreliable multicast message exchange Thread th = SendWithBackoff(message, udpClient); // Wait for resolve match as long a timeout has not expired byte[] resolveResponse = new byte[c_MaxUdpPacketSize]; EndPoint remoteEP = new IPEndPoint(IPAddress.Any, 0); int responseLength; DpwsDiscoClientProcessor soapProcessor = new DpwsDiscoClientProcessor(m_version); while (true) { // Since MF sockets does not have an IOControl method catch 10054 to get around the problem // with Upd and ICMP. try { // Wait for response responseLength = udpClient.ReceiveFrom(resolveResponse, c_MaxUdpPacketSize, SocketFlags.None, ref remoteEP); } catch (SocketException se) { if ((SocketError)se.ErrorCode == SocketError.ConnectionReset) { Thread.Sleep(100); continue; } if((SocketError)se.ErrorCode == SocketError.TimedOut) break; throw se; } // If we received process resolve match if (responseLength > 0) { System.Ext.Console.Write("ResolveMatches Response From: " + ((IPEndPoint)remoteEP).Address.ToString()); System.Ext.Console.Write(resolveResponse); try { WsWsaHeader header = new WsWsaHeader(); XmlReader reader = WsSoapMessageParser.ParseSoapMessage(resolveResponse, ref header, m_version); WsMessage msg = new WsMessage(header, null, WsPrefix.Wsdp); msg.Reader = reader; resolveMatch = soapProcessor.ProcessResolveMatch(msg, messageID, (IPEndPoint)remoteEP, messageCheck); if (resolveMatch != null) break; } catch (Exception e) { System.Ext.Console.Write(e.Message); } } } th.Join(); } // Display results if (resolveMatch == null) System.Ext.Console.Write("Resolve timed out."); return resolveMatch; }
/// <summary> /// Sends a Probe request and parses ProbeMatches responses. /// </summary> /// <param name="filters"> /// A DpwsServiceTypes object containing a collection of types a service must support to signal a match. /// Null = any type. /// </param> /// <param name="maxProbeMatches"> /// An integer representing the maximum number of matches to reveive within the timout period. Pass 0 to receive /// as many matches as possible before the timeout expires. /// </param> /// <param name="timeout"> /// An integer specifying a request timeout in milliseconds. Pass -1 to wait ReceiveTimeout. /// </param> /// <remarks> /// A Probe is used to discover services on a network. The Probe method sends a UDP request to the /// Dpws multicast address, 239.255.255.250:3702. Any service that implements types specified in the /// filters parameter should respond with a ProbeMatches message. The ProbeMatches mesgage is unicast /// back to the that client that made the request. If a null filter is supplied any Dpws complient /// service should reply with a ProbeMatches reponse. Probe waits DpwsDiceoveryCleint.ReceiveTimout /// for probe matches. /// </remarks> /// <returns> /// A collection of ProbeMatches objects. A ProbeMatch object contains endpoint details used /// used to locate the actual service on a network and the types supported by the service. /// </returns> public DpwsServiceDescriptions Probe(DpwsServiceTypes filters, int maxProbeMatches, int timeout) { // Build the probe request message WsMessageCheck messageCheck = new WsMessageCheck(); string messageID = null; WsMessage probeRequest = BuildProbeRequest(DiscoVersion.DiscoveryWellKnownAddress, filters, out messageID); DpwsServiceDescriptions probeMatches = new DpwsServiceDescriptions(); System.Ext.Console.Write(""); System.Ext.Console.Write("Sending Probe:"); System.Ext.Console.Write(probeRequest.Body as byte[]); System.Ext.Console.Write(""); // Create a new UdpClient using(Socket udpClient = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)) { IPEndPoint localEP = new IPEndPoint(IPAddress.Parse(WsNetworkServices.GetLocalIPV4Address()), m_discoResponsePort); udpClient.Bind(localEP); // Very important - Set default multicast interface for the underlying socket udpClient.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastInterface, (int)WsNetworkServices.GetLocalIPV4AddressValue()); udpClient.ReceiveTimeout = timeout; // Random back off implemented as per soap over udp specification // for unreliable multicast message exchange Thread th = SendWithBackoff((byte[])probeRequest.Body, udpClient); // Create probe matches collection and set expiration loop timer byte[] probeResponse = new byte[c_MaxUdpPacketSize]; EndPoint remoteEP = new IPEndPoint(IPAddress.Any, 0); int responseLength; DpwsDiscoClientProcessor soapProcessor = new DpwsDiscoClientProcessor(m_version); while (true) { // Since MF sockets does not have an IOControl method catch 10054 to get around the problem // with Upd and ICMP. try { // Wait for response responseLength = udpClient.ReceiveFrom(probeResponse, c_MaxUdpPacketSize, SocketFlags.None, ref remoteEP); } catch (SocketException se) { if ((SocketError)se.ErrorCode == SocketError.ConnectionReset) { Thread.Sleep(100); continue; } // Timeout if ((SocketError)se.ErrorCode == SocketError.TimedOut) break; throw se; } // If we received process probe match if (responseLength > 0) { System.Ext.Console.Write(""); System.Ext.Console.Write("ProbeMatches Response From: " + ((IPEndPoint)remoteEP).Address.ToString()); System.Ext.Console.Write(probeResponse); // Process the response try { WsWsaHeader header = new WsWsaHeader(); XmlReader reader = WsSoapMessageParser.ParseSoapMessage(probeResponse, ref header, m_version); WsMessage msg = new WsMessage(header, null, WsPrefix.Wsdp); msg.Reader = reader; DpwsServiceDescriptions tempMatches = soapProcessor.ProcessProbeMatch(msg, messageID, (IPEndPoint)remoteEP, messageCheck); if (tempMatches != null) { int count = maxProbeMatches < tempMatches.Count ? maxProbeMatches : tempMatches.Count; for (int i = 0; i < count; i++) { probeMatches.Add(tempMatches[i]); } maxProbeMatches -= count; } } catch (Exception e) { System.Ext.Console.Write(""); System.Ext.Console.Write(e.Message); System.Ext.Console.Write(""); } // If maxProbeRequest is set check count if (maxProbeMatches <= 0) { break; } } } th.Join(); } // Display results if (probeMatches.Count == 0) System.Ext.Console.Write("Probe timed out."); else System.Ext.Console.Write("Received " + probeMatches.Count + " probeMatches matches."); return probeMatches; }
/// <summary> /// Listens for Udp request on 239.255.255.250:3702 /// </summary> /// <remarks>On initialization it sends a Discovery Hello message and listens on the Ws-Discovery /// endpoint for a request. When a request arrives it starts a UdpProcess thread that processes the message. /// The number of UdpProcessing threads are limited by the Device.MaxUdpRequestThreads property. /// </remarks> public void Listen() { // Create a duplicate message tester. WsMessageCheck messageCheck = new WsMessageCheck(40); // Create remote endpoint reference, start listening byte[] buffer = new byte[c_MaxUdpPacketSize]; int size; bool threadPoolDepletedFlag = false; m_servicesRunning = !m_requestStop; while (!m_requestStop) { try { // If threads ara availble receive next message. If we are waiting on threads let the socket // buffer request until we get a thread. This will work until the reveice buffer is depleted // at which time request will be dropped if (m_threadManager.ThreadsAvailable == true) { threadPoolDepletedFlag = false; EndPoint remoteEP = new IPEndPoint(IPAddress.Any, 0); size = m_udpReceiveClient.ReceiveFrom(buffer, c_MaxUdpPacketSize, SocketFlags.None, ref remoteEP); // If the stack is set to ignore request from this address do so if (this.IgnoreRequestFromThisIP == true && ((IPEndPoint)remoteEP).Address.ToString() == WsNetworkServices.GetLocalIPV4Address()) { continue; } if (size > 0) { byte[] soapMessage = new byte[size]; Array.Copy(buffer, soapMessage, size); System.Ext.Console.Write("UDP Request From: " + remoteEP.ToString()); System.Ext.Console.Write(new String(System.Text.Encoding.UTF8.GetChars(soapMessage))); // Try to get a processing thread and process the request m_threadManager.StartNewThread(new WsUdpMessageProcessor(m_serviceEndpoints, soapMessage, (IPEndPoint)remoteEP, messageCheck)); } else { System.Ext.Console.Write("UDP Receive returned 0 bytes"); } } else { if (threadPoolDepletedFlag == false) { System.Ext.Console.Write("Udp service host waiting for a thread..."); threadPoolDepletedFlag = true; } } } catch (SocketException se) { // Since the MF Socket does not have IOControl that would be used to turn off ICMP notifications // for UDP, catch 10054 and try to continue if (se.ErrorCode == 10054) continue; } catch (Exception e) { System.Ext.Console.Write(e.Message + " " + e.InnerException); } Thread.Sleep(100); } }
/// <summary> /// Creates an empty instance of the UdpProcess class. /// </summary> public WsUdpMessageProcessor(WsServiceEndpoints serviceEndpoints, byte[] soapMessage, IPEndPoint remoteEP, WsMessageCheck messageCheck) { m_serviceEndpoints = serviceEndpoints; m_soapMessage = soapMessage; m_remoteEP = remoteEP; m_messageCheck = messageCheck; }
/// <summary> /// Use to send a resolve request to the ws-discovery address and receive a resolve match. /// </summary> /// <param name="message">A byte array containing a the resolve message.</param> /// <param name="messageID"> /// A string containing the message ID of a resolve request. This ID will be used to validate against /// a ResolveMatch received if it don't match, the ResolveMatch is discarded. /// </param> /// <param name="timeout"> /// A DateTime value containing the length of time this request will wait for resolve match. /// until the timeout value has expired. /// </param> /// <returns>A resolve match object.</returns> private DpwsServiceDescription SendResolveRequest(byte[] message, string messageID, long timeout) { WsMessageCheck messageCheck = new WsMessageCheck(); System.Ext.Console.Write(""); System.Ext.Console.Write("Sending Resolve:"); System.Ext.Console.Write(new string(new UTF8Encoding().GetChars(message))); // Create a new UdpClient Socket udpClient = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); IPEndPoint localEP = new IPEndPoint(IPAddress.Any, m_discoResponsePort); udpClient.Bind(localEP); // Very important - Set default multicast interface for the underlying socket byte[] ipBytes = IPAddress.Parse(WsNetworkServices.GetLocalIPV4Address()).GetAddressBytes(); long longIP = (long)((ipBytes[0] + (ipBytes[1] << 0x08) + (ipBytes[2] << 0x10) + (ipBytes[3] << 0x18)) & 0xFFFFFFFF); udpClient.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastInterface, (int)longIP); // Random back off implemented as per soap over udp specification // for unreliable multicast message exchange SendWithBackoff(message, udpClient); // Wait for resolve match as long a timeout has not expired DpwsServiceDescription resolveMatch = null; byte[] resolveResponse = new byte[c_MaxUdpPacketSize]; EndPoint remoteEP = new IPEndPoint(IPAddress.Any, 0); int responseLength; long endTime = (long)(DateTime.Now.Ticks + (timeout * 10000)); DpwsDiscoClientProcessor soapProcessor = new DpwsDiscoClientProcessor(); while (DateTime.Now.Ticks < endTime) { if (udpClient.Available > 0) { // Since MF sockets does not have an IOControl method catch 10054 to get around the problem // with Upd and ICMP. try { // Wait for response responseLength = udpClient.ReceiveFrom(resolveResponse, c_MaxUdpPacketSize, SocketFlags.None, ref remoteEP); } catch (SocketException se) { if (se.ErrorCode == 10054) continue; throw se; } // If we received process resolve match if (responseLength > 0) { System.Ext.Console.Write(""); System.Ext.Console.Write("ResolveMatches Response From: " + ((IPEndPoint)remoteEP).Address.ToString()); System.Ext.Console.Write(new String(System.Text.Encoding.UTF8.GetChars(resolveResponse))); try { resolveMatch = soapProcessor.ProcessResolveMatch(resolveResponse, messageID, (IPEndPoint)remoteEP, messageCheck); if (resolveMatch != null) break; } catch (Exception e) { System.Ext.Console.Write(""); System.Ext.Console.Write(e.Message); System.Ext.Console.Write(""); } } } Thread.Sleep(10); } udpClient.Close(); udpClient = null; // Display results if (resolveMatch == null) System.Ext.Console.Write("Resolve timed out."); return resolveMatch; }
/// <summary> /// Sends a Probe request and parses ProbeMatches responses. /// </summary> /// <param name="filters"> /// A DpwsServiceTypes object containing a collection of types a service must support to signal a match. /// Null = any type. /// </param> /// <param name="maxProbeMatches"> /// An integer representing the maximum number of matches to reveive within the timout period. Pass 0 to receive /// as many matches as possible before the timeout expires. /// </param> /// <param name="timeout"> /// An integer specifying a request timeout in milliseconds. Pass -1 to wait ReceiveTimeout. /// </param> /// <remarks> /// A Probe is used to discover services on a network. The Probe method sends a UDP request to the /// Dpws multicast address, 239.255.255.250:3702. Any service that implements types specified in the /// filters parameter should respond with a ProbeMatches message. The ProbeMatches mesgage is unicast /// back to the that client that made the request. If a null filter is supplied any Dpws complient /// service should reply with a ProbeMatches reponse. Probe waits DpwsDiceoveryCleint.ReceiveTimout /// for probe matches. /// </remarks> /// <returns> /// A collection of ProbeMatches objects. A ProbeMatch object contains endpoint details used /// used to locate the actual service on a network and the types supported by the service. /// </returns> public DpwsServiceDescriptions Probe(DpwsServiceTypes filters, int maxProbeMatches, int timeout) { // Build the probe request message WsMessageCheck messageCheck = new WsMessageCheck(); string messageID = null; byte[] probeRequest = BuildProbeRequest(DiscoVersion.WellKnownAddress, filters, out messageID); System.Ext.Console.Write(""); System.Ext.Console.Write("Sending Probe:"); System.Ext.Console.Write(new string(new UTF8Encoding().GetChars(probeRequest))); System.Ext.Console.Write(""); // Create a new UdpClient Socket udpClient = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); IPEndPoint localEP = new IPEndPoint(IPAddress.Any, m_discoResponsePort); udpClient.Bind(localEP); // Very important - Set default multicast interface for the underlying socket byte[] ipBytes = IPAddress.Parse(WsNetworkServices.GetLocalIPV4Address()).GetAddressBytes(); long longIP = (long)((ipBytes[0] + (ipBytes[1] << 0x08) + (ipBytes[2] << 0x10) + (ipBytes[3] << 0x18)) & 0xFFFFFFFF); udpClient.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastInterface, (int)longIP); // Random back off implemented as per soap over udp specification // for unreliable multicast message exchange SendWithBackoff(probeRequest, udpClient); // Create probe matches collection and set expiration loop timer DpwsServiceDescriptions probeMatches = new DpwsServiceDescriptions(); byte[] probeResponse = new byte[c_MaxUdpPacketSize]; EndPoint remoteEP = new IPEndPoint(IPAddress.Any, 0); int responseLength; // Multiply receive time by 10000 to convert milliseconds to 100 nano ticks long endTime = (long)(DateTime.Now.Ticks + (timeout < 0 ? m_receiveTimeout * 10000 : timeout * 10000)); DpwsDiscoClientProcessor soapProcessor = new DpwsDiscoClientProcessor(); // Build probe matches collection as long as timeout has not expired int noReceived = 0; while (DateTime.Now.Ticks < endTime) { if (udpClient.Available > 0) { // If maxProbeRequest is set check count if (maxProbeMatches > 0) { if (noReceived > maxProbeMatches) break; } // Since MF sockets does not have an IOControl method catch 10054 to get around the problem // with Upd and ICMP. try { // Wait for response responseLength = udpClient.ReceiveFrom(probeResponse, c_MaxUdpPacketSize, SocketFlags.None, ref remoteEP); } catch (SocketException se) { if (se.ErrorCode == 10054) continue; throw se; } // If we received process probe match if (responseLength > 0) { System.Ext.Console.Write(""); System.Ext.Console.Write("ProbeMatches Response From: " + ((IPEndPoint)remoteEP).Address.ToString()); System.Ext.Console.Write(new String(System.Text.Encoding.UTF8.GetChars(probeResponse))); // Process the response try { DpwsServiceDescriptions tempMatches = soapProcessor.ProcessProbeMatch(probeResponse, messageID, (IPEndPoint)remoteEP, messageCheck); if (tempMatches != null) { int count = tempMatches.Count; for (int i = 0; i < count; i++) { probeMatches.Add(tempMatches[i]); } } } catch (Exception e) { System.Ext.Console.Write(""); System.Ext.Console.Write(e.Message); System.Ext.Console.Write(""); } // Increment the number received counter ++noReceived; } } else { Thread.Sleep(1); } } udpClient.Close(); udpClient = null; // Display results if (probeMatches == null) System.Ext.Console.Write("Probe timed out."); else System.Ext.Console.Write("Received " + probeMatches.Count + " probeMatches matches."); return probeMatches; }