簡體   English   中英

如何使用WS-Discovery規范在Java中發現網絡上的ONVIF設備?

[英]How to use WS-Discovery spec to discover ONVIF devices on a network in Java?

我正在嘗試使用一些Java代碼來發現ONVIF設備。 具體來說,我正在嘗試獲取他們的設備服務地址(我相信這只是他們的IP地址?),正如ONVIF核心規范 (在第4.3節中)指出的那樣:“成功的發現將提供設備服務地址。設備服務地址,它可以通過設備服務接收詳細的設備信息...”。 我的目標是最終獲得網絡上ONVIF設備的詳細信息。 通常,我也在尋找有關使用ONVIF規范的指導。

我仍然對Web服務世界(以及一般而言的網絡)還是陌生的,所以請原諒我。 但是,我本人為此付出了很多努力:我閱讀了許多ONVIF核心規范ONVIF應用程序員指南WS-Discovery規范 如果可以的話,我將總結一下我所知道的,以便您可以告訴我我是否處在正確的軌道上:

  1. “ Web服務”是使用平台和語言無關的Web服務標准(例如IP網絡上的XML,SOAP和WSDL)的標准技術的名稱。 基本思想是我們希望能夠從任何編程語言中調用有效的方法/函數(服務)。
  2. Web服務通常托管在服務器上。 但是在ONVIF用例中,Web服務提供商是ONVIF設備(例如IP攝像機)。 因此,為了以任何語言與設備進行交互,我們可以使用Web服務操作/調用,因為Web服務調用可以以任何語言實現。
  3. XML是數據描述語法(之所以使用,是因為它與語言無關;任何語言都可以解析它)。 SOAP是用於來回獲取注入SOAP的XML文檔的通信協議(基本上是進行方法調用)。 WSDL用於描述服務(它是Web服務接口的基於XML的描述)。 我已經在此處下載了用於設備管理的WSDL,並通過WSDL編譯器wsimport (由JDK提供)從WSDL中生成了Java類,以便在我的代碼中使用。 但我知道調用這些方法將在設備發現之后進行,對嗎?
  4. 根據WS-Discovery規范發現ONVIF設備。 您發送了Probe消息,並且符合Probe約束的設備會發回ProbeMatch消息,如《 ONVIF應用程序程序員指南》第13和14頁所述

這是我開始感到困惑的地方。 我如何用Java發送此消息? 《 ONVIF應用程序程序員指南》在第15頁提供了一些偽代碼,但我不知道如何實現它。 該指南明確指出了該指南中的第4.3.1節。 我了解“作用域”和“類型”只是您可以嵌入探針中的約束,但不是必需的(根據WS Discovery規范的第5頁)。 因為我想發現所有設備,所以我認為啟動時不需要任何約束,對嗎?

因此,該指南還在第110頁上提供了用於發現的示例SOAP消息。 從中刪除類型聲明(因為我不想使用該約束),我知道要發送的SOAP消息是(我相信嗎?):

<?xml version="1.0" encoding="UTF-8"?>
<e:Envelope xmlns:e="http://www.w3.org/2003/05/soap-envelope"
 xmlns:w="http://schemas.xmlsoap.org/ws/2004/08/addressing"
 xmlns:d="http://schemas.xmlsoap.org/ws/2005/04/discovery"
 xmlns:dn="http://www.onvif.org/ver10/network/wsdl">
 <e:Header>
 <w:MessageID>uuid:84ede3de-7dec-11d0-c360-f01234567890</w:MessageID>
 <w:To e:mustUnderstand="true">urn:schemas-xmlsoap-org:ws:2005:04:discovery</w:To>
 <w:Action
a:mustUnderstand="true">http://schemas.xmlsoap.org/ws/2005/04/discovery/Pr
obe</w:Action>
 </e:Header>
 <e:Body>
 <d:Probe>
 </d:Probe>
 </e:Body>
</e:Envelope>   

而且我也了解WS-Discovery技術使用的239.255.255.259地址和UDP端口3702 ...但這就是我的意思。 如何將SOAP消息發送到Java中的該地址和端口? 我如何讀取響應(我我會以SOAP注入的XML文檔的形式返回ProbeMatch消息,因此我需要解析該XML以獲得XAddrs ,但不確定)。 我是否需要以某種方式將該SOAP消息的UDP廣播發送到該地址?

TL; DR :我相信要進行ONVIF設備發現,我需要將上述SOAP消息發送至UDP端口3702上的地址239.255.255.259。我不知道如何在Java中進行此操作,只是在尋找一些指導; 我什至不確定自己是否在進行設備發現的正確道路上。

使用CXF WSDiscoveryClient您可以探測ONVIF設備。
默認情況下,WSDiscoveryClient使用WS-discovery 1.1,而ONVIF使用WS-discovery 1.0,因此您需要啟用WS-discovery 1.0。
一個簡短的實現可能是:

import java.util.List;
import javax.xml.ws.EndpointReference;
import org.apache.cxf.ws.discovery.WSDiscoveryClient;

public class Main 
{
    public static void main(String[] args) 
    {
        WSDiscoveryClient client = new WSDiscoveryClient();
        client.setVersion10(); // use WS-discovery 1.0
        client.setDefaultProbeTimeout(1000); // timeout 1s

        System.out.println("Probe:" + client.getAddress());
        List<EndpointReference> references = client.probe();

        System.out.println("Nb answsers:" + references.size());
        for (EndpointReference ref : references)
        {
            System.out.println(ref.toString());
        }
    }
}

經過反復試驗,我找到了另一個解決方案。 在大多數情況下,mpromonet可能是最好的選擇,我只是想避免使用像Apache這樣的大型依賴項。 我還認為,僅使用一些簡單的UDP消息傳遞就應該可行。

此解決方案還基於SO用戶Thomas 在這里的有用代碼。 我基本上只是通過刪除線程來簡化他的代碼,並添加了一些注釋。 同樣,他的解決方案可能比我的解決方案(表現更好)更好。 但是,我的初學者(例如我)可能更容易理解。

這是代碼:

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.net.*;

import javax.xml.namespace.QName;
import javax.xml.soap.*;
import java.util.*;

public class ONVIFDeviceDiscoveryFIN {

   // Following constants are related to Discovery process
   public static final int WS_DISCOVERY_TIMEOUT = 4000; // 4 seconds. Time to wait to receive a packet
   public static final int WS_DISCOVERY_PORT = 3702; 
   public static final String WS_DISCOVERY_ADDRESS_IPv4 = "239.255.255.250";

   // note that the probe below MUST be given a unique urn:uuid. Devices will NOT reply if the urn:uuid is not unique! 
   public static final String WS_DISCOVERY_PROBE_MESSAGE = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" + 
        "<soap:Envelope xmlns:soap=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:tds=\"http://www.onvif.org/ver10/device/wsdl\" xmlns:tns=\"http://schemas.xmlsoap.org/ws/2005/04/discovery\" xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\">\r\n" + 
        "   <soap:Header>\r\n" + 
        "      <wsa:Action>http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe</wsa:Action>\r\n" + 
        "      <wsa:MessageID>urn:uuid:5e1cec36-03b9-4d8b-9624-0c5283982a00</wsa:MessageID>\r\n" + 
        "      <wsa:To>urn:schemas-xmlsoap-org:ws:2005:04:discovery</wsa:To>\r\n" + 
        "   </soap:Header>\r\n" + 
        "   <soap:Body>\r\n" + 
        "      <tns:Probe>\r\n" + 
        "         <tns:Types>tds:Device</tns:Types>\r\n" + // Constraint to find just ONVIF devices hopefully? Recall we are sending a probe on the 192.168.0.50 network; if we have no constraints, it would find everything there! WS-Discovery generally is for much more than ONVIF, like printers and stuff
        "      </tns:Probe>\r\n" + 
        "   </soap:Body>\r\n" + 
        "</soap:Envelope>";

   private static ArrayList<String> getResponsesToProbe(String uuid) throws IOException{
       // TODO: add in ability to send scope and type constraints
       // NOTE: We do need to know the address of the network interface to discover devices on...
       // Function composes and sends a Probe to discover devices on the network. uuid is the urn:uuid to put in the probe. Functions returns all the SOAP-Infused XML responses (all the ProbeMatches).

       // give the probe a unique urn:uuid (we must do this for each probe!). This is generated outside function
       final String probe = WS_DISCOVERY_PROBE_MESSAGE.replaceAll("<wsa:MessageID>urn:uuid:.*</wsa:MessageID>", "<wsa:MessageID>urn:uuid:" + uuid + "</wsa:MessageID>");

       // set up the "sender and receiver"; this is the socket that we send our probe from, and where we receive back the ProbeMatch responses.
       // NOTE:  that we do need to know the address of the network interface to discover devices on... (port could be anything)
       final int port = 55000;
       DatagramSocket senderAndReceiver = new DatagramSocket(port, InetAddress.getByName("192.168.0.50")); // so you do need to know the address of your network interface to discover devices on...
       senderAndReceiver.setSoTimeout(WS_DISCOVERY_TIMEOUT);

       // send the probe 
       DatagramPacket probeMsg = new DatagramPacket(probe.getBytes(), probe.length(), InetAddress.getByName(WS_DISCOVERY_ADDRESS_IPv4), WS_DISCOVERY_PORT);
       senderAndReceiver.send(probeMsg);

       // read in the responses
       ArrayList<String> responses = new ArrayList(); // this is the collection of all SOAP-infused XML ProbeMatch responses
       byte[] receiverBuffer = new byte[8192];
       DatagramPacket receiverPacket = new DatagramPacket(receiverBuffer, receiverBuffer.length); // this is the packet that receive the response in. Get's updated with the next response on each call to .receive()
       while (true) {
           try {
               senderAndReceiver.receive(receiverPacket);
               responses.add(new String(receiverPacket.getData()));

           } catch (SocketTimeoutException e) {
               // System.out.println("Socket read timeout; taken to mean that there is no more responses -- i.e., no more Probe Matches");
               break;
           }
       }

       // close the socket
       senderAndReceiver.close();

       return responses;

   }

   public static void main(String[] args) throws IOException, SOAPException {


       final String uuid = UUID.randomUUID().toString(); // generate the uuid to add to the Probe message

       ArrayList<String> responses = getResponsesToProbe(uuid); // responses is a collection of all the SOAP-infused XML ProbeMatches . It's all of our responses to the probe; it's basically the devices we've discovered!

    }

}

有關使用此方法的一些注意事項:

  1. 要使用此解決方案,您需要知道“網絡接口”才能繼續查看。 在我的代碼中,這是192.168.0.50。 這是我要查找的相機所在的網絡。 要找到此位置,請在cmd提示符下運行arp -a命令(不確定在Mac或Linux上如何執行此操作),然后找到相機的IP。 它所屬的接口是您要用作“ 192.168.0.50”的接口。 以我的有限理解,這些接口基本上可以分割您的網絡,因此您需要選擇合適的接口來查找設備。 我認為(?)Thomas的代碼通過查找所有這些網絡接口避免了這個問題,這是在他的代碼的第81-100行中完成的。

  2. 發送時,您必須給探針一個唯一的UUID。 這是我這樣做的錯誤之一; 我在Probe(在WS_DISCOVERY_PROBE_MESSAGE )中使用了硬編碼的UUID進行了測試。 這將一次發現設備; 但是之后,如果您發送具有相同UUID的探測,則設備似乎根本不會回復。 您也不會收到任何錯誤響應,因此為什么我很難找出答案。 就像設備保留了所有收到的所有探針的UUID的內部日志一樣。 如果您發送帶有舊UUID的探測,它只會拒絕它。 至少,對於我正在測試的符合ONVIF的攝像機(AXIS M3045-V),情況就是如此。 我不確定ONVIF規范是否需要這種行為,但是至少在AXIS M3045-V中是明顯的。

  3. 注意:SOAP通常依賴HTTP進行傳輸。 但是這里我們在UDP之上使用它。

希望這對嘗試做類似事情的人有所幫助。 讓我知道我有什么可以幫助的; 此時,我已經閱讀了大量文檔,因此我可以伸出援手!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM