服務端的開發相對來說比客戶端稍微難一點,也就是給填充相關結構體的時候,需要一點一點的去查閱,驗證各個結構中各個成員各自代表什么意思,以及對應的功能需要是那個接口實現,這是開發服務端最頭疼的事情。(在開發過程中郁悶了好久,后面是通過搜索工具抓包海康設備來填充相關信息的)開始切入主題了,准備服務端的開發了。
同理需要前面生成的代碼,這個時候較之客戶端的開發,需要在代碼生成的時候之前生成的soapServer.c文件了,當放在客戶端測試目錄下用makefile編譯的時候,你可能會很驚訝,怎么這么多錯誤,這么多函數報錯,而且都是沒有定義呢?
別緊張,這些接口就是服務端開發需要實現的!即使開發不需要使用,但是根據onvif協議,也是需要給函數一個實現體的,具體的這些函數就需要看看soapServer.c文件里的soap_serve_request函數,這里我貼出來一部分如下:
- ifndef WITH_NOSERVEREQUEST
- SOAP_FMAC5 int SOAP_FMAC6 soap_serve_request(struct soap *soap)
- {
- soap_peek_element(soap);
- if (!soap_match_tag(soap, soap->tag, "wsdd:Hello"))
- return soap_serve___wsdd__Hello(soap);
- if (!soap_match_tag(soap, soap->tag, "wsdd:Bye"))
- return soap_serve___wsdd__Bye(soap);
- if (!soap_match_tag(soap, soap->tag, "wsdd:Probe"))
- return soap_serve___wsdd__Probe(soap);
- if (!soap_match_tag(soap, soap->tag, "wsdd:ProbeMatches"))
- return soap_serve___wsdd__ProbeMatches(soap);
- if (!soap_match_tag(soap, soap->tag, "wsdd:Resolve"))
- return soap_serve___wsdd__Resolve(soap);
- if (!soap_match_tag(soap, soap->tag, "wsdd:ResolveMatches"))
- return soap_serve___wsdd__ResolveMatches(soap);
- if (!soap_match_tag(soap, soap->tag, "ns1:GetSupportedActions"))
- return soap_serve___ns1__GetSupportedActions(soap);
- if (!soap_match_tag(soap, soap->tag, "ns1:GetActions"))
- return soap_serve___ns1__GetActions(soap);
- if (!soap_match_tag(soap, soap->tag, "ns1:CreateActions"))
- return soap_serve___ns1__CreateActions(soap);
- if (!soap_match_tag(soap, soap->tag, "ns1:DeleteActions"))
- return soap_serve___ns1__DeleteActions(soap);
- if (!soap_match_tag(soap, soap->tag, "ns1:ModifyActions"))
- return soap_serve___ns1__ModifyActions(soap);
- if (!soap_match_tag(soap, soap->tag, "ns1:GetServiceCapabilities"))
- .
- .
- .
- //當然了,后來還有很長,很多了,具體實際開發可以根據需要來實現對應的函數體就好了
后面還有很多系列函數,都是在對應了接口中調用了需要服務端實現的接口函數,代碼框架只是有申明,沒有實現,所以開發服務端的第一步,就是需要根據這里,把所以報錯沒有實現的函數全部實現掉,但是給一個函數體就好,后期開發再確定需要實現那些功能,再一個一個的接口具體實現,所以接下來就是寫代碼了,即使是拷貝復制工作,你也會有一種手要斷了的趕覺的。我記得當時我就是一個整個下午在ctrl+ v 選中,然后p 粘貼.最后弄完眼睛都花了。
因為每個函數的可以用相同的函數體來實現,所以寫一個簡單宏來代替是比較方面而且直觀的方法,我的實現如下:
- #define ONVIF_NOT_IMPLEMENTED_FUNC(soap, namespaces) \
- if( namespaces != NULL) \
- soap_set_namespaces(soap, namespaces); \
- printf("Func: %s, Path: %s \n", __func__, soap->path); \
- return soap_receiver_fault_subcode(soap, "test:Action Not Supported", "Test: Not Implemented ", "The requested action is not implemented ");
好了有了前面的准備工作開始寫發現函數了,設備端的回復搜索的接口函數為__wsdd__Probe__wsdd__Probe,實現如下:
- int SOAP_FMAC6 __wsdd__Probe(struct soap* soap, struct wsdd__ProbeType *wsdd__Probe)
- {
- printf(" \n ##### Start __wsdd__Probe ##### \n");
- char _IPAddr[64] = {0};
- char _HwId[256] = {0};
- wsdd__ProbeMatchesType ProbeMatches;
- ProbeMatches.ProbeMatch = (struct wsdd__ProbeMatchType *)soap_malloc(soap, sizeof(struct wsdd__ProbeMatchType));
- memset(ProbeMatches.ProbeMatch, 0, sizeof(struct wsdd__ProbeMatchType));
- ProbeMatches.ProbeMatch->XAddrs = (char *)soap_malloc(soap, sizeof(char) * 256);
- memset(ProbeMatches.ProbeMatch->XAddrs, '\0', sizeof(char) * 256);
- ProbeMatches.ProbeMatch->Types = (char *)soap_malloc(soap, sizeof(char) * 256);
- memset(ProbeMatches.ProbeMatch->Types, '\0', sizeof(char) * 256);
- ProbeMatches.ProbeMatch->wsa__EndpointReference.ReferenceProperties = (struct wsa__ReferencePropertiesType*)soap_malloc(soap,sizeof(struct wsa__ReferencePropertiesType));
- memset(ProbeMatches.ProbeMatch->wsa__EndpointReference.ReferenceProperties, 0, sizeof(struct wsa__ReferencePropertiesType));
- ProbeMatches.ProbeMatch->wsa__EndpointReference.ReferenceParameters = (struct wsa__ReferenceParametersType*)soap_malloc(soap,sizeof(struct wsa__ReferenceParametersType));
- memset(ProbeMatches.ProbeMatch->wsa__EndpointReference.ReferenceParameters, 0, sizeof(struct wsa__ReferenceParametersType));
- ProbeMatches.ProbeMatch->wsa__EndpointReference.ServiceName = (struct wsa__ServiceNameType*)soap_malloc(soap,sizeof(struct wsa__ServiceNameType));
- memset(ProbeMatches.ProbeMatch->wsa__EndpointReference.ServiceName, 0, sizeof(struct wsa__ServiceNameType));
- ProbeMatches.ProbeMatch->wsa__EndpointReference.PortType = (char **)soap_malloc(soap, sizeof(char *) * 256);
- memset(ProbeMatches.ProbeMatch->wsa__EndpointReference.PortType, 0, sizeof(char *) * 256);
- ProbeMatches.ProbeMatch->wsa__EndpointReference.__any = (char **)soap_malloc(soap, sizeof(char*) * 256);
- memset(ProbeMatches.ProbeMatch->wsa__EndpointReference.__any, 0, sizeof(char*) * 256);
- ProbeMatches.ProbeMatch->wsa__EndpointReference.__anyAttribute = (char *)soap_malloc(soap, sizeof(char) * 256);
- memset(ProbeMatches.ProbeMatch->wsa__EndpointReference.__anyAttribute, 0, sizeof(char) * 256);
- ProbeMatches.ProbeMatch->wsa__EndpointReference.Address = (char *)soap_malloc(soap, sizeof(char) * 256);
- memset(ProbeMatches.ProbeMatch->wsa__EndpointReference.Address, 0, sizeof(char) * 256);
- //這里是一個uid,我在實際開發的過程中是的設備的本身mac地址,這里我取了一個mac地址的地址
- strcpy(_HwId, "urn:uuid:20131228-AABB-CCDD-EEFF-010203040506");
- //這是是需要回復給給客戶端的基本信息.設備的ip地址以及通信端口
- sprintf(_IPAddr, "http://%d.%d.%d.%d:%d/onvif/device_service", 192,168,12,103, 8899);
- ProbeMatches.__sizeProbeMatch = 1;
- ProbeMatches.ProbeMatch->Scopes = (struct wsdd__ScopesType*)soap_malloc(soap, sizeof(struct wsdd__ScopesType) * ProbeMatches.__sizeProbeMatch);
- memset(ProbeMatches.ProbeMatch->Scopes, 0, sizeof(struct wsdd__ScopesType) * ProbeMatches.__sizeProbeMatch);
- //Scopes MUST BE
- ProbeMatches.ProbeMatch->Scopes->__item =(char *)soap_malloc(soap, 1024);
- memset(ProbeMatches.ProbeMatch->Scopes->__item, '\0', 1024);
- strcat(ProbeMatches.ProbeMatch->Scopes->__item, "onvif://www.onvif.org/type/Network_Video_Transmitter ");
- strcat(ProbeMatches.ProbeMatch->Scopes->__item, "onvif://www.onvif.org/type/video_encoder ");
- strcat(ProbeMatches.ProbeMatch->Scopes->__item, "onvif://www.onvif.org/type/audio_encoder ");
- strcat(ProbeMatches.ProbeMatch->Scopes->__item, "onvif://www.onvif.org/location/city/CSDN ");
- strcat(ProbeMatches.ProbeMatch->Scopes->__item, "onvif://www.onvif.org/name/csder ");
- strcat(ProbeMatches.ProbeMatch->Scopes->__item, "onvif://www.onvif.org/hardware/TEST_Onvif ");
- ProbeMatches.ProbeMatch->Scopes->MatchBy = NULL;
- strcpy(ProbeMatches.ProbeMatch->XAddrs, _IPAddr);
- strcpy(ProbeMatches.ProbeMatch->Types, wsdd__Probe->Types);
- printf("wsdd__Probe->Types=%s\n",wsdd__Probe->Types);
- ProbeMatches.ProbeMatch->MetadataVersion = 1;
- //ws-discovery規定 為可選項
- ProbeMatches.ProbeMatch->wsa__EndpointReference.ReferenceProperties->__size = 0;
- ProbeMatches.ProbeMatch->wsa__EndpointReference.ReferenceProperties->__any = NULL;
- ProbeMatches.ProbeMatch->wsa__EndpointReference.ReferenceParameters->__size = 0;
- ProbeMatches.ProbeMatch->wsa__EndpointReference.ReferenceParameters->__any = NULL;
- ProbeMatches.ProbeMatch->wsa__EndpointReference.PortType[0] = (char *)soap_malloc(soap, sizeof(char) * SMALL_256);
- //ws-discovery規定 為可選項
- strcpy(ProbeMatches.ProbeMatch->wsa__EndpointReference.PortType[0], "ttl");
- ProbeMatches.ProbeMatch->wsa__EndpointReference.ServiceName->__item = NULL;
- ProbeMatches.ProbeMatch->wsa__EndpointReference.ServiceName->PortName = NULL;
- ProbeMatches.ProbeMatch->wsa__EndpointReference.ServiceName->__anyAttribute = NULL;
- ProbeMatches.ProbeMatch->wsa__EndpointReference.__any[0] = (char *)soap_malloc(soap, sizeof(char) * SMALL_256);
- strcpy(ProbeMatches.ProbeMatch->wsa__EndpointReference.__any[0], "Any");
- strcpy(ProbeMatches.ProbeMatch->wsa__EndpointReference.__anyAttribute, "Attribute");
- ProbeMatches.ProbeMatch->wsa__EndpointReference.__size = 0;
- strcpy(ProbeMatches.ProbeMatch->wsa__EndpointReference.Address, _HwId);
- soap->header->wsa__To = (char *)"http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous";
- soap->header->wsa__Action = (char *)"http://schemas.xmlsoap.org/ws/2005/04/discovery/ProbeMatches";
- soap->header->wsa__RelatesTo = (struct wsa__Relationship*)soap_malloc(soap, sizeof(struct wsa__Relationship));
- soap->header->wsa__RelatesTo->__item = soap->header->wsa__MessageID;
- soap->header->wsa__RelatesTo->RelationshipType = NULL;
- soap->header->wsa__RelatesTo->__anyAttribute = NULL;
- soap->header->wsa__MessageID =(char *)soap_malloc(soap, sizeof(char) * 256);
- strcpy(soap->header->wsa__MessageID,_HwId+4); //前面四個字節可以是不需要的
- if (SOAP_OK == soap_send___wsdd__ProbeMatches(soap, "http://", NULL, &ProbeMatches))
- {
- // printf("send ProbeMatches success !\n");
- return SOAP_OK;
- }
- printf("[%d] soap error: %d, %s, %s\n", __LINE__, soap->error, *soap_faultcode(soap), *soap_faultstring(soap));
- return soap->error;;
- }
搜索的回復函數本分就完成了,現在需要要的工作就是在設備端開啟一個udp的socket了。為了不影響設備端其他的業務,所以建議設備端另外開一個線程讓socket運行起來,因為這個是一個一直循環的操作了,基本的代碼如下:
- int Onvif_DeviceDiscovery()
- {
- struct soap udp_soap;
- int retval = -1;
- soap_init1(&udp_soap, SOAP_IO_UDP | SOAP_IO_FLUSH);
- udp_soap.connect_flags = SO_BROADCAST;
- udp_soap.port = 3702;
- soap_set_namespaces( &udp_soap, namespaces);
- SOAP_SOCKET udp_sock = -1;
- udp_soap->omode = SOAP_IO_UDP;
- udp_soap->bind_flags = SO_REUSEADDR;
- udp_sock = soap_bind(udp_soap, NULL, udp_soap->port, 100);
- if( (udp_sock < 0) && (udp_sock == SOAP_INVALID_SOCKET))
- {
- close(udp_sock);
- printf(" soap_bind failed! %s \n", strerror(errno));
- return -1;
- }
- //這個接口設置一些廣播的屬性值,下面也有實現提出,
- retval = udp_add_multicast(&udp_soap);
- if(retval != 0 )
- {
- printf(" udp add multicast failed: %s \n", strerror(errno));
- return -1;
- }
- //每次都是在此循環中接收客戶端發過來的廣播請求,然后服務端調用__wsdd__Probe函數,返回服務端的一些基本信息
- while(1)
- {
- if( soap_serve( &udp_soap ) != 0)
- {
- soap_print_fault(&udp_soap, stderr);
- }
- soap_destroy(&udp_soap);
- soap_end( &udp_soap);
- }
- soap_done(&udp_soap);
- return 0;
- }
- int udp_add_multicast( struct soap* socksoap)
- {
- // set a route for multicast traffic
- // 這個執行一個系統命令,之前一直無法被搜索到,后來查了資料才知道需要啟動下
- system("route add -net 224.0.0.0 netmask 224.0.0.0 eth0");
- int loop;
- int retval = -1;
- struct ip_mreq mreqcon;
- loop = 1;
- //設置組播的屬性
- retval = setsockopt(socksoap->master, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop));
- if(retval != 0)
- {
- printf("setsockopt: IP_MULTICAST_LOOP failed! %s\n ", strerror(errno));
- return -1;
- }
- //綁定組播的ip地址
- mreqcon.imr_multiaddr.s_addr = inet_addr("239.255.255.250");
- mreqcon.imr_interface.s_addr = htonl(INADDR_ANY);
- if( (signed int )mreqcon.imr_multiaddr.s_addr == -1)
- {
- printf("239.255.255.250 not a legal multicast address! %s\n", strerror(errno));
- return -1;
- }
- retval = setsockopt(socksoap->master, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreqcon, sizeof(mreqcon));
- if( retval != 0 )
- {
- printf("setsockopt: IP_ADD_MEMBERSHIP failed! %s\n ", strerror(errno));
- return -1;
- }
- return 0;
- }
完成這些代碼之后,通過onvif搜索工具,搜索的結果圖如下

看到基本信息也就是上面代碼中填寫了!設備端的發現功能也就實現了!
縱觀前后,其實設備端的發現只是在已經有的代碼框架基礎上操作兩步就好!
1 創建socket,搭建廣播接收回復服務
2 實現__wsdd__Probe函數!
提供大家一個不錯的網站:onvif server
