两种方法,一种是使用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);//多播地址
//向多播组发送数据
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的内容就可以了.