關於ONVIF的廣播,有客戶端搜索和服務端發現的區別:客戶端向固定的網段和固定的端口發送廣播消息,服務端在對應的端口回復廣播請求消息
本文首先介紹客戶端如何進行廣播的已經對廣播回復的信息的基本處理。
客戶端這里處理相對服務端比較復雜一點,需要注意幾個地方:
1 廣播的ip和端口號(注意這個端口號和通信的端口是不一樣的,通信端口號可以自己隨意定一個,但是這個廣播的是ONVIF協議定好公用的)
ip:239:255:255:250 port:3702
2 MessageID ,此ID每次都需要不同,不然每次只有第一次能搜索到設備,第二次就不行了!一般情況下都是區uuid的隨機值的,但是我在開發的過程是去的一個隨機值加上自己設備的mac地址組合的
3 soap_send___wsdd__Probe函數,發送廣播信息,通過它來判斷是否發送成功,確認是否繼續下一步操作進行接收消息
4 soap_recv___wsdd__ProbeMatches函數,此函數是通過廣播后,設備通過onvif協議回復了設備的一些基本信息,客戶端通過循 環調用此函數來接多個設備回復的各個設備的一些基本信息包括設備ip以及通信的prot
知道了這幾個基本的注意點和前篇介紹的代碼生成框架,接下來就是敲代碼了。該main函數登場了
- <span style="font-size:14px;">/*
- * =====================================================================================
- *
- * Filename: main.c
- * Description: 簡單例程測試:客戶端通過ONVIF協議搜索前端設備,
- * Created: 2013年12月26日 12時17分48秒
- * Compiler: gcc
- * Author: max_min_,
- *
- * =====================================================================================
- */
- #include "wsdd.h"
- #include <stdio.h>
- static struct soap* ONVIF_Initsoap(struct SOAP_ENV__Header *header, const char *was_To, const char *was_Action, int timeout)
- {
- struct soap *soap = NULL;
- unsigned char macaddr[6];
- char _HwId[1024];
- unsigned int Flagrand;
- soap = soap_new();
- if(soap == NULL)
- {
- printf("[%d]soap = NULL\n", __LINE__);
- return NULL;
- }
- soap_set_namespaces( soap, namespaces);
- //超過5秒鍾沒有數據就退出
- if (timeout > 0)
- {
- soap->recv_timeout = timeout;
- soap->send_timeout = timeout;
- soap->connect_timeout = timeout;
- }
- else
- {
- //如果外部接口沒有設備默認超時時間的話,我這里給了一個默認值10s
- soap->recv_timeout = 10;
- soap->send_timeout = 10;
- soap->connect_timeout = 10;
- }
- soap_default_SOAP_ENV__Header(soap, header);
- // 為了保證每次搜索的時候MessageID都是不相同的!因為簡單,直接取了隨機值
- srand((int)time(0));
- Flagrand = rand()%9000 + 1000; //保證四位整數
- macaddr[0] = 0x1; macaddr[1] = 0x2; macaddr[2] = 0x3; macaddr[3] = 0x4; macaddr[4] = 0x5; macaddr[5] = 0x6;
- sprintf(_HwId,"urn:uuid:%ud68a-1dd2-11b2-a105-%02X%02X%02X%02X%02X%02X",
- Flagrand, macaddr[0], macaddr[1], macaddr[2], macaddr[3], macaddr[4], macaddr[5]);
- header->wsa__MessageID =(char *)malloc( 100);
- memset(header->wsa__MessageID, 0, 100);
- strncpy(header->wsa__MessageID, _HwId, strlen(_HwId));
- if (was_Action != NULL)
- {
- header->wsa__Action =(char *)malloc(1024);
- memset(header->wsa__Action, '\0', 1024);
- strncpy(header->wsa__Action, was_Action, 1024);//"http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe";
- }
- if (was_To != NULL)
- {
- header->wsa__To =(char *)malloc(1024);
- memset(header->wsa__To, '\0', 1024);
- strncpy(header->wsa__To, was_To, 1024);//"urn:schemas-xmlsoap-org:ws:2005:04:discovery";
- }
- soap->header = header;
- return soap;
- }
- int ONVIF_ClientDiscovery( )
- {
- int HasDev = 0;
- int retval = SOAP_OK;
- wsdd__ProbeType req;
- struct __wsdd__ProbeMatches resp;
- wsdd__ScopesType sScope;
- struct SOAP_ENV__Header header;
- struct soap* soap;
- const char *was_To = "urn:schemas-xmlsoap-org:ws:2005:04:discovery";
- const char *was_Action = "http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe";
- //這個就是傳遞過去的組播的ip地址和對應的端口發送廣播信息
- const char *soap_endpoint = "soap.udp://239.255.255.250:3702/";
- //這個接口填充一些信息並new返回一個soap對象,本來可以不用額外接口,
- // 但是后期會作其他操作,此部分剔除出來后面的操作就相對簡單了,只是調用接口就好
- soap = ONVIF_Initsoap(&header, was_To, was_Action, 5);
- soap_default_SOAP_ENV__Header(soap, &header);
- soap->header = &header;
- soap_default_wsdd__ScopesType(soap, &sScope);
- sScope.__item = "";
- soap_default_wsdd__ProbeType(soap, &req);
- req.Scopes = &sScope;
- req.Types = ""; //"dn:NetworkVideoTransmitter";
- retval = soap_send___wsdd__Probe(soap, soap_endpoint, NULL, &req);
- //發送組播消息成功后,開始循環接收各位設備發送過來的消息
- while (retval == SOAP_OK)
- {
- retval = soap_recv___wsdd__ProbeMatches(soap, &resp);
- if (retval == SOAP_OK)
- {
- if (soap->error)
- {
- printf("[%d]: recv error:%d,%s,%s\n", __LINE__, soap->error, *soap_faultcode(soap), *soap_faultstring(soap));
- retval = soap->error;
- }
- else //成功接收某一個設備的消息
- {
- HasDev ++;
- if (resp.wsdd__ProbeMatches->ProbeMatch != NULL && resp.wsdd__ProbeMatches->ProbeMatch->XAddrs != NULL)
- {
- printf(" ################ recv %d devices info #### \n", HasDev );
- printf("Target Service Address : %s\r\n", resp.wsdd__ProbeMatches->ProbeMatch->XAddrs);
- printf("Target EP Address : %s\r\n", resp.wsdd__ProbeMatches->ProbeMatch->wsa__EndpointReference.Address);
- printf("Target Type : %s\r\n", resp.wsdd__ProbeMatches->ProbeMatch->Types);
- printf("Target Metadata Version : %d\r\n", resp.wsdd__ProbeMatches->ProbeMatch->MetadataVersion);
- sleep(1);
- }
- }
- }
- else if (soap->error)
- {
- if (HasDev == 0)
- {
- printf("[%s][%d] Thers Device discovery or soap error: %d, %s, %s \n", __func__, __LINE__, soap->error, *soap_faultcode(soap), *soap_faultstrin
- retval = soap->error;
- }
- else
- {
- printf(" [%s]-[%d] Search end! It has Searched %d devices! \n", __func__, __LINE__, HasDev);
- retval = 0;
- }
- break;
- }
- }
- soap_destroy(soap);
- soap_end(soap);
- soap_free(soap);
- return retval;
- }
- int main(void )
- {
- //組播接口
- if (ONVIF_ClientDiscovery() != 0 )
- {
- printf("discovery failed!\n");
- return -1;
- }
- return 0;
- }
- </span>
運行測試結果如下圖
可以看到注意信息就是http://172.18.13.127:9000/onvif/device_service這句了!設備的IP地址以及通信端口都有了,第三個沒有端口,也就是使用默認的端口80了,海康和大華的貌似都是用80端口
到這里差不多客戶端的搜索就完成了,剩下只是把搜索到的設備信息解析出來,然后保存起來!提供給客戶端使用了,我在實際開發的過程中是通過調用外層接口傳遞一個回調函數指針進去,然后匹配信息出來,給客戶端!
搜索使用的源碼下載地址:onvif客戶端搜索