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

两种方法,一种是使用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的内容就可以了.
  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 13
    评论
实现一个 C# 的 Onvif 客户端,你需要遵循以下步骤: 1. 引用 Onvif 标准的 WSDL 文件 Onvif 标准定义了一组 Web Services Description Language (WSDL) 文件,你需要将这些文件添加到你的 C# 项目中。这些文件包含了 Onvif 标准所定义的服务接口和数据类型。 2. 生成客户端代理类 使用 Visual Studio 或者命令行工具,基于 WSDL 文件生成客户端代理类,这样你就可以使用 C# 代码调用 Onvif 标准定义的服务接口。 3. 创建客户端对象 使用生成的客户端代理类创建客户端对象,你需要提供 Onvif 设备的 IP 地址、端口号、用户名和密码等信息。 4. 调用服务接口 使用客户端对象调用 Onvif 标准定义的服务接口,例如获取设备信息、控制云台、获取视频流等操作。 下面是一个简单的示例代码,演示了如何使用 C# 实现 Onvif 客户端: ```csharp using System; using System.ServiceModel; using onvif.services; namespace onvif_client { class Program { static void Main(string[] args) { // Onvif 设备的 IP 地址、端口号、用户名和密码 string address = "http://192.168.1.100/onvif/device_service"; string username = "admin"; string password = "admin"; // 创建服务绑定和客户端对象 var binding = new BasicHttpBinding(); var endpoint = new EndpointAddress(address); var client = new DeviceClient(binding, endpoint); // 设置验证凭据 client.ClientCredentials.UserName.UserName = username; client.ClientCredentials.UserName.Password = password; try { // 调用服务接口 var response = client.GetDeviceInformation(new GetDeviceInformationRequest()); // 打印设备信息 Console.WriteLine("Manufacturer: {0}", response.Manufacturer); Console.WriteLine("Model: {0}", response.Model); Console.WriteLine("Serial Number: {0}", response.SerialNumber); } catch (Exception ex) { Console.WriteLine("Error: {0}", ex.Message); } finally { // 关闭客户端 client.Close(); } } } } ``` 以上示例代码演示了如何使用 Onvif 标准定义的 GetDeviceInformation 接口获取设备信息。你可以根据需要调用其他的服务接口,实现更加复杂的操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值