ONVIF 客户端简单实现Discovery,实现设备搜索.

来源:互联网 发布:淘宝电子面单平台 编辑:程序博客网 时间:2024/04/28 12:38

两种方法,一种是使用GSOAP的库WS-Discovery,另一种是直接自己写一个SOCKET多播消息。

第一种解XML和定制XML方便,发送的消息也是标准的Discovery协议,

这种方法能收到XML信息的回应,但解不出来!现在得自己解析这一部分.

第二种就需要自己写XML,对协议了解需要更多点。

第三种直接用onvif WSDL里生成的生成的类型soap_recv___tdn__*********这样的,这个和第一种一样,也还是解不出XML

A: ONVIF GSOAP客户端使用WS-Discovery

使用GSOAP来Discovery,以下是步骤

1:用的到文件(gSOAP WS-Discovery 2.8 Stable手册里有说明)
plugin目录
threads.c
threads.h
wssaapi.c
wssaapi.h
wsddapi.c
wsddapi.h

import目录
wsdd.h

2:用WS目录里的WS-Discovery.wsdl生成代码(当然用ONVIF的remotediscovery也可以,只是产生的就很大了)
./wsdl2h -cgye -o discovery.h -t WS-typemap.dat WS-Discovery.wsdl 
./soapcpp2 -C -c -2 -n -pdiscovery discovery.h -I../import

3:一些代码的修改
第2步完成的会生成一些代码,需要改一下 wsddapi.h里的一个头文件的引用

Wsddapi.h 和 wsaapi.h 里有一个 #include "soapH.h",soapH是我们默认生成的名字,因为第二步我改了 生成代码文件的名字,改成discovery了,就是那个 soapcpp2  -n选项
所以需要 Wsddap.h  #include "soapH.h"改成 #include "discoveryH.h" 

还改了生成文件:discoveryStub.h里的 SOAP_NAMESPACE_OF_wsdd 
原来的:
#define SOAP_NAMESPACE_OF_wsdd "http://docs.oasis-open.org/ws-dd/ns/discovery/2009/01"

改成:

#define SOAP_NAMESPACE_OF_wsdd "http://schemas.xmlsoap.org/ws/2005/04/discovery"

因为我对照了ONVIF测试工具发的,这个,和上面生成的不一样,就改了一下.

4:WSDD需要自己实现的几个方法
这个在手册gSOAP WS-Discovery 2.8 Stable里也有说:
以下方法:就是相当于,事件处理函数,如,发后了HELLO后,最后会调用wsdd_event_Hello这个方法

void wsdd_event_ProbeMatches(struct soap *soap, unsigned int InstanceId, const char *SequenceId, unsigned int 
MessageNumber, const char *MessageID, const char *RelatesTo, struct wsdd__ProbeMatchesType *matches)
{
return;
}

void wsdd_event_ResolveMatches(struct soap *soap, unsigned int InstanceId, const char *SequenceId, unsigned int 
MessageNumber, const char *MessageID, const char *RelatesTo, struct wsdd__ResolveMatchType *match)
{
return;
}

void wsdd_event_Hello(struct soap *soap, unsigned int InstanceId, const char *SequenceId, unsigned int MessageNumber, 
const char *MessageID, const char *RelatesTo, const char *EndpointReference, const char *Types, const char *Scopes, const 
char *MatchBy, const char *XAddrs, unsigned int MetadataVersion)
{
return;
}

void wsdd_event_Bye(struct soap *soap, unsigned int InstanceId, const char *SequenceId, unsigned int MessageNumber, const 
char *MessageID, const char *RelatesTo, const char *EndpointReference, const char *Types, const char *Scopes, const char 
*MatchBy, const char *XAddrs, unsigned int *MetadataVersion)
{
return;
}

soap_wsdd_mode wsdd_event_Probe(struct soap *soap, const char *MessageID, const char *ReplyTo, const char *Types, const 
char *Scopes, const char *MatchBy, struct wsdd__ProbeMatchesType *matches)
{
printf("MessageID:%s",MessageID);
return SOAP_WSDD_MANAGED;
}

soap_wsdd_mode wsdd_event_Resolve(struct soap *soap, const char *MessageID, const char *ReplyTo, const char 
*EndpointReference, struct wsdd__ResolveMatchType *match)
{
return SOAP_WSDD_MANAGED;
}


5:在GOSAP里使用
完成以上的内容,应该就可以用了,因为是多播,所以在发送时要设置
soap.connect_flags = SO_BROADCAST;


像下面的一样,在偏译时打开 -DDEBUG,应该在 RECV.LOG目录里就可以看到收到的消息了.
int main(){
   struct soap soap; 
   
   char *msg_uuid = NULL;
   soap_set_namespaces(&soap,namespaces); 
   soap_init(&soap); 
   msg_uuid = soap_wsa_rand_uuid(&soap);
   
   soap.connect_flags = SO_BROADCAST;
   printf("msg_uuid:%s\n",msg_uuid);

   soap_wsdd_Probe(&soap,SOAP_WSDD_MANAGED,SOAP_WSDD_TO_TS,"soap.udp://239.255.255.250:3702",msg_uuid,NULL,DS_TYPES,"",NULL);

   for(i = 0;i<10;i++){
    soap_recv___wsdd__Probe(&soap,&__wsdd__probe_recv);
sleep(1);
   }
   
   soap_destroy(&soap);
   soap_end(&soap);
   soap_done(&soap);

}


收到的是一个XML信息,可以定义一个GSOAP的一个回调,如下:

int recv_callback(struct soap *gsoap,char *xml,int len){
printf("xml:%s\n",xml);
return 0;
}

soap.frecv = recv_callback;

或者,直接调用soap_wsdd_Probe读 soap.buff

以上的过程,事后记录,可能会少了一些细节。
soap_wsdd_Probe是填充BODY和HEADER的内容,而soap_send___wsdd__Probe就是把BUFF发送出去,有的朋友直接调用了soap_send___wsdd__Probe,所以发送的XML就是空的body.

B: 自己写SOCKET来接发消息:

上面这样用,我们就不用写什么代码什么的,如果自己写一个多播发送的消息,似乎更简单,生成的代码也小很多:像下面这个方法用来发送多播消息:


int sendtomt(){
 int ret;
 int s;
 int i=1;
 char buffer[2048] = {0};
 struct sockaddr_in Multi_addr;//多播地址
 struct sockaddr_in client_addr;
 s=socket(AF_INET,SOCK_DGRAM,0);//建立数据报套接字
 if(s<0){
   perror("socket error");
   return -1;
 }
 Multi_addr.sin_family=AF_INET;
 Multi_addr.sin_port=htons(MCAST_PORT);//多播端口
 Multi_addr.sin_addr.s_addr=inet_addr(MCAST_ADDR);//多播地址

//向多播组发送数据


  int size=sendto(s,buff_define,strlen(buff_define),0,(struct sockaddr*)&Multi_addr,sizeof(Multi_addr));
  if(size<0){
perror("sendto error");
  }
  sleep(1);
  i++;
  int len=sizeof(client_addr);
  for(i = 0;i<10;i++){
  memset(buffer,0,sizeof(buffer));
memset(&client_addr,0,sizeof(struct sockaddr));
  size=recvfrom(s,buffer,2047,0,(struct sockaddr*)&client_addr,&len);
printf("=============:%s,%d=================i:%d======\nbuffer:%s\n",inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port), i,buffer);
sleep(1);
  }
 close(s);
}


发送的内容,这个内容就要根据自己实际的东东改了:
char *buff_define = "<?xml version=\"1.0\" encoding=\"utf-8\"?> \
<Envelope \
xmlns:dn=\"http://www.onvif.org/ver10/network/wsdl\" \ 
xmlns=\"http://www.w3.org/2003/05/soap-envelope\"> \
<Header>\
<wsa:MessageID xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\">uuid:4e7ca33c-1f95-4cba-a05a-
0079b3ba927f</wsa:MessageID>\
<wsa:To xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\">urn:schemas-xmlsoap-
org:ws:2005:04:discovery</wsa:To>\
<wsa:Action 
xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\">http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe</wsa:
Action>\
</Header> \
<Body> \
<Probe \
xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" \
xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" \
xmlns=\"http://schemas.xmlsoap.org/ws/2005/04/discovery\"> \
<Types>dn:NetworkVideoTransmitter</Types> \
<Scopes /> \
</Probe> \
</Body> \
</Envelope>";



这个方法,麻烦的是,你还需要自己解收到的XML,但我们可以找一个轻量级的XML库来做,因为不需要改写XML,这个库能解XML的内容就可以了.