2013년 4월 23일 화요일

WS-Discovery using gSoap


WS-Discovery using gSoap


OASIS에서 제공하는 WS-Discovery WSDL 문서로 스텁코드를 생성하여 gSoap Plug-in을 링크하여 Target Service device를 구현한다.
gsoap에서 만들어 놓은 import/wsdd10.h를 바로 써도 되지만 원리를 좀 더 알기 위함이다.
gsoap-2.8.6 버젼으로 진행한다.
2012년 6월 21일  신C


  1.   헤더파일 만들기
    plug-in과 연동하기 위해 gsoap에서 수정한 사항을 생성된 wsa5.h에 적용

    1) WS-Addressing 헤더 만들기
    1. wsdl2h -cgye -o wsa5.h -t WS-typemap.dat  http://www.w3.org/2005/08/addressing/ws-addr.xsd
        - Removed //gsoapopt
        - Removed xsd__boolean declaration
        - Added the following directive to import WS-Addressing namespace:
          //gsoap wsa5  schema import:    http://www.w3.org/2005/08/addressing
        This ensures that the WS-Addressing schemas are not copied into the
        generated WSDL by soapcpp2 but are referenced with schema import in the
        generated WSDL.
        - Added //gsoap wsa5  schema namespace2: http://schemas.xmlsoap.org/ws/2004/08/addressing
        - Added #define SOAP_WSA_2005
        - Added SOAP_ENV__Header struct
   - wsa5__Action 에서 "wsa5__"제거

           2) WS-Discovery 헤더 만들기
  1. wsdl2h -cyex -o wsdiscovery.h -t WS-typemap.dat http://docs.oasis-open.org/ws-dd/discovery/1.1/os/wsdd-discovery-1.1-wsdl-os.wsdl http://docs.oasis-open.org/ws-dd/discovery/1.1/os/wsdd-discovery-1.1-schema-os.xsd 
        - Removed //gsoapopt
        - Changed //gsoap wssd schema namespace directive to import directive
        - Added #import "wsdx.h" at the end of these definitions
                - wsdd__Types, wsdd__Scopes, wsdd_XAddrs, wsdd__MetadataVersion 에서 "wsdd__" 제거
                - wsdd__QNameListType* -> wsdd__QNameListType로 수정

wsdd-discovery-1.1-wsdl-os.wsdl 에는 Binding element가 정의 되지 않았다. 메소드가 WS-Addressing을 이용하므로  독립성을 주기 위한 것 같다.
  • wsdl에 biding이 없으므로, 헤더에 서비스 메소드 정의가 생성되지 않는다(#import wsdx.h 없을시)
  • 스키마를 인자로 주지 않으면 생성되는 헤더에 자동으로 #import 지시어가 붙는다.

2.  스텁코드 만들기
  1.        soapcpp2 -cx  wsdiscovery.h

3. 이벤트 핸들러를 wsdiscovery.c에 정의한다.
soap-2.8/gsoap/doc/wsdd/html/wsdd_0.html#wsdd_2 을 참고한다.
       wsdd_event_Hello()
wsdd_event_Bye()
wsdd_event_Probe()
wsdd_event_ProbeMatches()
wsdd_event_Resolve()
wsdd_event_ResolveMatches()

4. 링크하기
soapC.c  soapClient.c  : stub code, soapServer.c에서 정의한 서버측 함수는  wsddapi.c에 새로 정의 한 것으로 쓰므로 필요없다.
wsddapi.c wsaapi.c (thread.h wsddapi.h wsaapi.h)                         : gsoap plug-in 
wsdiscovery.c                                     : user defined  event handlers

5. 발생한 문제
1)  wsddapi.c 에서 UDP multicast packet을 못 받는다.
- 원인 : 기본 설정이 TCP socket 으로 listen 한다.
- 해결방법: UDP 멀티캐스팅 되도록 소켓에 옵션을 준다.
- gsoap user guide 18.7 SOAP-over-UDP Multicast Receiving Server 참조
- src/gsoap/samples/udp/udpserver.c 참조

2) 윈도우용 디커버리 응용프로그램 SiONVIFManager, Milestion Xprotect 에서 device scan 했을 때,
"Method 'd:Probe' not implemented: method name or namespace not recognized" 가 나온다.
- 원    인 : 네임스페이스가 다르다. 클라이언트  http://schemas.xmlsoap.org/ws/2005/04/discovery  그리고 서버는 "http://docs.oasis-open.org/ws-dd/ns/discovery/2009/01"
- 해결방법 : wsdiscovery.h에 //gsoap wsdd  schema namespace2:    http://schemas.xmlsoap.org/ws/2005/04/discovery 를 추가하여 namespace table을 변경한다.
-참          고 : gsoap-2.8/gsoap/doc/soapdoc2.html
//gsoap namespace-prefix schema namespace2: namespace-URI-pattern
If the first namespace does not match the inbound parsed XML, then the second will be tried. This pattern may contain '*' multichar wildcards and '-' single chard wildcards. This allows two or more namespace versions to be handled by the same namespace prefix.

3) soap_wsdd_ProbeMatches(..) 을 실행하면 -1이 리턴된다.
- 원       인 :  endpoint 파라미터 형식이 틀렸다.
- 해결방법:  Probe event 에서 클라이언트 접속정보를 저장한 뒤 "soap.udp:// addresss:port" 형식으로 endpoint를 준다.
- 참고1:  gsoap-2.8/gsoap/doc/wsdd/html/wsdd_0.html
In ad-hoc mode, ProbeMatches or ResolveMatches responses are NOT sent automatically. In ad-hoc mode the responses can be returned by adding code to the event handler or from anywhere in the main program, for example after soap_wsdd_listen. When responses are to be returned from the event handler or from the main program, you should invoke soap_wsdd_ProbeMatches and soap_wsdd_ResolveMatches to explicitly send unicast messages with the match(es) back to the clients. The WS-Addressing ReplyTo address can be used as the return address (when not anonymous), or by using the peer's host information that is accessible in the soap->peer and soap->peerlen members.

참고2: gsoap-2.8/gsoap/doc/soapdoc2.html (18 SOAP-over-UDP)
Client-side messages with SOAP-over-UDP endpoint URLs (soap.udp://...) will be automatically transmitted as datagrams. Server-side applications should set the SOAP_IO_UDP mode flag to accept UDP requests, e.g. using soap_init1 or soap_set_mode.
The maximum message length for datagram packets is restricted by the buffer size SOAP_BUFLEN, which is 65536 by default, unless compiled with WITH_LEAN to support small-scale embedded systems. For UDP transport SOAP_BUFLEN must not exceed the maximum UDP packet size 65536 (the size of datagram messages is constrained by the UDP packet size 216=65536 as per UDP standard). You can use gzip compression to reduce the message size, but note that compressed SOAP-over-UDP is a gSOAP-specific feature because it is not part of the SOAP-over-UDP specification. The SOAP-over-UDP specification relies on WS-Addressing. The wsa.h file in the import directory defines the WS-Addressing elements for client and server applications.

4)  soap_wsdd_ProbeMatches() 전송뒤 Client에서 Device service request가 없다.
- 원       인 : ONVIF Core Specification ver2.2 (2 Normative references)에서 Discovery는 "http://schemas.xmlsoap.org/ws/2005/04/discovery 으로 되어있다.
                 결국 Probe에서 Namespace가 맞지 않는 것도 당연하다.
- 해결 방법 :  라이브러리를 "http://schemas.xmlsoap.org/ws/2005/04/discovery" 로 구현해야 한다.


5. Soap-2.8.6 plug-in 을  http://schemas.xmlsoap.org/ws/2005/04/discovery 버젼으로 수정하기

(1)  WS-typemap.dat에서   프리픽스 바인딩을 수정한다. wsddapi.c 에서 네임스페이스가 wsdd 프리픽스가 있기 때문이다.
  1.  wsdd11  = <http://docs.oasis-open.org/ws-dd/ns/discovery/2009/01>
     wsdd  = <http://schemas.xmlsoap.org/ws/2005/04/discovery>

(2) WS-addressing을 2004년 버젼으로 헤더 만들기
  1. wsdl2h -cgye -o wsa.h -t WS-typemap.dat  http://schemas.xmlsoap.org/ws/2004/08/addressing
   - //gsoapopt 제거
    - //gsoap wsa schema namespace:--- 에서 //gsoap wsa schema import:--으로 수정
    - 구조체 추가
    struct SOAP_ENV__Header
    {
             _wsa__MessageID  wsa__MessageID 0;
             _wsa__RelatesTo *wsa__RelatesTo 0;
             _wsa__From      *wsa__From      0;
      mustUnderstand _wsa__ReplyTo   *wsa__ReplyTo   0;
      mustUnderstand _wsa__FaultTo   *wsa__FaultTo   0;
      mustUnderstand _wsa__To         wsa__To        0;
      mustUnderstand _wsa__Action     wsa__Action    0;
    };

(3) WS-typemap.dat에서 2004/08/addressing의 프리픽스가 wsa로 되어 있다. wsaapi.c에서 2004년버젼일 경우 wsa__ 함수로 구현되어 있으므로
     wsdx.h의 프리픽스를 wsa로 수정한다.
wsa5__xxx ->  wsa__xxx

(4) WS-Discovery을 2005년 버젼으로 헤더 만들기
  1. wsdl2h -cyex -o wsdiscovery.h -t WS-typemap.dat http://schemas.xmlsoap.org/ws/2005/04/discovery/ws-discovery.wsdl http://schemas.xmlsoap.org/ws/2005/04/discovery/ws-discovery.xsd
        - Removed //gsoapopt
        - Changed //gsoap wssd schema namespace directive to import directive
        - Added #import "wsdx.h" at the end of these definitions
                - wsdd__Types, wsdd__Scopes, wsdd_XAddrs, wsdd__MetadataVersion 에서 "wsdd__" 제거
                - wsdd__QNameListType* -> wsdd__QNameListType로 수정

(5) 스텁코드 만들기
  1. soapcpp2 -cx  wsdiscovery.h

        (6) wsddapi.c 수정하기
          - soap_wsdd_Probe(...): struct wsdd__ResolveMatchesType res -> struct __wsdd__ProbeMatches res
          - soap_wsdd_Resolve(...):  struct wsdd__ResolveMatchesType res -> struct __wsdd__ResolveMatches res
  
          - const char *Action = "http://docs.oasis-open.org/ws-dd/ns/discovery/2009/01/ProbeMatches"
             ->  const char *Action = "http://schemas.xmlsoap.org/ws/2005/04/discovery/ProbeMatches";
      
        - const char *Action = "http://docs.oasis-open.org/ws-dd/ns/discovery/2009/01/ResolveMatches";
           ->  const char *Action = "http://schemas.xmlsoap.org/ws/2005/04/discovery/ResolveMatches";
      
        - const char *Action = "http://docs.oasis-open.org/ws-dd/ns/discovery/2009/01/Hello";
          -> "http://schemas.xmlsoap.org/ws/2005/04/discovery/Hello";
        
          - const char *Action = "http://docs.oasis-open.org/ws-dd/ns/discovery/2009/01/Bye";
             ->"http://schemas.xmlsoap.org/ws/2005/04/discovery/Bye";
        
             - To = "urn:docs-oasis-open-org:ws-dd:ns:discovery:2009:01";
              ->  "urn://schemas.xmlsoap.org:ws:2005:04:discovery";

          - match.wsa5__*  -> match.wsa__*

(7) 동작 확인(Probe/ProbeMatch)
- Milestone Xprotect (http://www.milestonesys.com/freetrialdownloads/) : 장치인식확인됨.
- schille ONVIF Device Manager (http://www.server001.net/PUBLIC/) :  장치인식확인됨.

(8) 디스커버리 클라이언트 - 서버 테스트용
1) Probe/Types 에 들어갈 데이터의 namespace를 wsdd.namap에 추가
    - {"dn", "http://www.onvif.org/ver10/network/wsdl", NULL, NULL}

2) 서버에서 만들어진 스텁코드와 event handler를 구현한 wsdiscovery_clnt.c , x86에서 컴파일 및 링크.
- soapC.c wsddapi.c wsaapi.c stdsoap2.c   wsdiscovery_clnt.c soapClient.c soapServer.c

3) Trouble shoting
   문제: Client에서 3702로 바인딩 되지 않은 소켓으로 전송하니까 임의로 지정된 포트가 지정되어, 서버에서 소스포트로 응답하면 클라이트는
                        3702포트로 수신 대기중이므로 데이터를 받지 못함.
   해결 : 요청(probe/resolve)을 보낸 소켓을 응답까지 받고 세션을 끝내야한다.
                     mode=SOAP_WSDD_MANAGED;

                문제 :Test Client 에서 보낸 Probe/Resolve에 내가 구현한 서버만 응답하고, Axis IP Camera에서 응답을 안한다.
                원인: WSA <faultcode>를 보니 아래와 같아 확인해 보니 Action에 오타가 있었음.
                        <faultcode>wsa:InvalidMessageINformationHeader</faultcode>
                        <faul string> A message information header is not valid and the message cannot be processed</faul string>
                        <detail>wsa:Action</detail>
                  해결:스키마 어드레스 오타 수정  wsddapi.c에서
                              const char *Action = "http://schemas.xmlsoap.org/ws/2005/04/discovery/*"

   문제: Probe/Resolve 요청시 응답이 없으면 무한정 기다린다.
   원인: 타임아웃 설정 안했음.
   해결: 타임아웃 설정 Probe/Resolve , soap context 초기화 후 설정.
            wsdiscovery_clnt.c 에서 soap->accept_timeout = soap->recv_timeout = soap->send_timeout = 1;

     문제: Probe 요청 후 ProbeMatch가 여러개 들어오면 하나만 받는다.
    원인: soap_wsdd_Probe(..) in wsddapi.c 가 하나의 ProbeMatch만 받게 되어있다.
    해결: soap_recv___wsdd__ProbeMatches(soap, &res) 를 타임 아웃될때 까지 루프를 돌아 받는다.



댓글 2개:

  1. 안녕하세요?
    혹시 gSOAP에 포함된 wsdd10.h 를 이용해서
    WS-Discovery를 구현해보셨나요?

    간단한 절차좀 알려주세요~ @.@

    답글삭제
  2. 미안합니다. 너무 오래 지났네요 ^^;; 제 기억이 맞을지 확실하지 않지만..
    (5) 스텁코드 만들기: soapcpp2 -cx (wsdiscovery.h -> wsdd10.h)으로 하시면 될것 같아요.

    답글삭제