这篇主要是对gsoap编程的一个简单介绍,以及消息结构的简单那剖析,最后简单介绍了一下discover的实现。
网上有两种比较好的参考代码,我自提供一种 :下载(discover)第二种是onvif_stream
切忌:使用gsoap一定要联网!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1
(三)gsoap编程简述
这一部分只是对gsoap最基本的框架做个简单的介绍,并不是这部分不重要,这部分很重要,而是这部分很重要,要想使用gsoap工具进行编程,学习和研究soapdoc2.pdf文件时必须的。
1、服务器端的基本构架
(1)最基本的程序框架
1. int main()
2. {
3. struct soap *soap;
4. ...
5. soap =soap_new();//定义并初始化环境变量
6. soap_init(soap);
7. soap_server(soap);
8. if(!soap)//如果不能定义,退出
9. ...
10. soap_call_ns__method1(soap,...);//调用远程函数
11. ...
12. soap_call_ns__method2(soap,...);//调用另一个远程函数
13. ...
14. soap_destroy(soap);//cleanup allocated class instance
15. soap_end(soap);//清除环境变量
16. soap_done(soap);//detachcontext (last use and no longer in scope)
17. free(soap);//释放环境变量空间
18.
19. }
(2)多线程的程序框架
20. int main()
21. {
22. struct soap soap1,soap2;
23. pthread t tid;
24. ...
25. soapinit(&soap1);
26. if (soapbind(&soap1, host, port, backlog) < 0) exit(1);
27. if (soapaccept(&soap1) < 0) exit(1);
28. pthreadcreate(&tid, NULL, (void*(*)(void*))soap serve, (void*)&soap1);
29. ...
30. soapinit(&soap2);
31. soap call nsmethod(&soap2, ...); // make a remote call
32. ...
33. soapend(&soap2);
34. ...
35. pthread join(tid,NULL); // wait for thread to terminate
36. soap end(&soap1);// release its data
37. }
2、初始化gsoap运行环境的常用函数
Function |
Description |
soap init(struct soap *soap) |
Initializes a runtime context |
soap init1(struct soap *soap, soap mode iomode) |
Initializes a runtime context and set in/out mode flags |
soap init2(struct soap *soap, soap mode imode, soap mode omode) |
Initializes a runtime context and set in/out mode flags |
struct soap *soap new() |
Allocates, initializes, and returns a pointer to a runtime context |
struct soap *soap new1(soap mode iomode) |
Allocates, initializes, and returns a pointer to a runtime context and set in/out mode flags |
struct soap *soap new2(soap mode imode, soap mode omode) |
Allocates, initializes, and returns a pointer to a runtime context and set in/out mode flags |
struct soap *soap copy(struct soap *soap) |
Allocates a new runtime context and copies a context (deep copy,i.e. the new context does not share any data with the other context) |
soap done(struct soap *soap) |
Reset, close communications, and remove callbacks |
soap free(struct soap *soap) |
Reset and deallocate the con-text created with soap new or soap copy |
(四)设备发现的功能的实现。
1、组播
(1)组播的基础知识
UDP相对于TCP来说,虽然是无连接,不可靠传输,但是可以实现组播,可以同时给多个主机发送数据,比如聊天室之类的应用,如果为每个用户之间都建立一个Tcp连接,而且每次发送的数据又是相同的,这样做使得程序开销大,而且占用内存多;另外,udp还有一个广播的服务,但是广播不能筛选,也就是说广播会向所有在同一子网的主机发送数据,这样无疑也增加了网络负担;这时我们可以考虑通过udp的组播来实现,下面就是对组播的一些总解:
组播的地址采用D类IP地址,范围是从 224.0.0.0 到 239.255.255.255,下面有几个保留地址,一般不作为用户使用的地址
224.0.0.1 - 该子网上的所有主机。
224.0.0.2 - 该子网上的所有路由器。
224.0.0.5 - 开放最短路径优先(Open Shortest Path First,OSPF)算法第2版,设计用于到达某个网络上的所有OSPF路由器。
224.0.0.6 - 开放最短路径优先算法第2版,设计用于到达某个网络上的所有OSPF指定的路由器。
224.0.0.9 - 路由信息协议(Routing Information Protocol,RIP)第2版。
224.0.1.1 - 网络时间协议(Network Time Protocol)。
(2)组播的流程
对组播的使用流程概括起来就是:创建套接字,绑定本地地址,加入组播,监听组播信息
具体化到客户端服务器就是:
服务器:创建udp套接字-->设置发送组播的地址和端口号(例如:224.20.20.2:9999)-->循环发送
客户端:创建udp套接字-->加入多播组(只有加入多播组,发送的数据才不会被丢弃)-->绑定本机IP地址和端口号(必须和发送端的地址和端口号一致,224.20.20.2:9999,其中如果端口号为0,则广播组播都可以接收)
再组播中会涉及到以下几个函数的使用,我们需要熟悉他常用个几个设置以及他们的功能是什么:
socket
setsockopt
其中我认为比较重要的我已经真理出了文档。
(3)组播的实现
在设备发现中组播代码的分析如下:
创建组播用的udp socket,绑定组播地址为239.255.255.250,端口为3702,因为ws-discovery的组播地址和端口就是为239.255.255.250和3702
首先是udp socket
1. int bind_server_udp1(int server_s) //将sock绑定到本机某端口上。
2.
3. {
4. struct sockaddr_in local_addr;
5. //这里申请了一个sockaddr_in类型的结构体,主要是我们填充本地地址信息
6. memset(&local_addr,0,sizeof(local_addr));
7. //对申请的本地信息结构体进行初始化
8. local_addr.sin_family =AF_INET;
9. // 协议族,ipv4
10. local_addr.sin_addr.s_addr= htonl(INADDR_ANY);
11. //如果将sin_addr设置为INADDR_ANY,则表示所有的IP地址,也即所有的计算机。
12. local_addr.sin_port =htons(3702);
13. // 填充服务器端口的信息把服务器端口转换成网络字节序
14. return bind(server_s,(struct sockaddr*)&local_addr,sizeof(local_addr));
15. //实现和套接字的绑定
16. }
17. static int create_server_socket_udp(void) //建立udp的套接字
18. {
19. int server_udp;
20. unsigned char one = 1;
21. int sock_opt = 1;
22.
23. //server_udp =socket(PF_INET, SOCK_DGRAM, 0);
24. server_udp =socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
25. //创建udp类型的套接字 udp使用SOCK_DGRAM第三个使用0是默认协议
26. if (server_udp == -1) {
27. printf("unable to create socket\n");
28. }
29.
30. /* reuse socket addr*/
31. if ((setsockopt(server_udp, SOL_SOCKET,SO_REUSEADDR, (void *) &sock_opt, sizeof (sock_opt)))== -1) {
32. printf("setsockopt\n");
33. } //设置允许重用本地地址和端口
34. if ((setsockopt(server_udp, IPPROTO_IP,IP_MULTICAST_LOOP,
35. &one, sizeof (unsigned char))) == -1){
36. printf("setsockopt\n");
37. } //禁止多播数据回送到本地loop接口.
38.
39. struct ip_mreq mreq;
40. mreq.imr_multiaddr.s_addr= inet_addr("239.255.255.250");
41. //设置多播组地址
42. mreq.imr_interface.s_addr= htonl(INADDR_ANY);
43. //设置加入接口的ip地址,这里是所有计算机
44. if(setsockopt(server_udp,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq))==-1){
45. perror("memberchip error\n");
46. } //把一个socket加入到一个多播组
47.
48. return server_udp;
49. }
2、ws-addressing
(1)ws-addressing的简述
WS-Addressing 即WebService 协议栈中的一项协议。显而易见,从"ws-Addressing"可以大概了解其用途:webservice 寻址。
为了更好的了解ws-addressing,我们先从 SOAP 说起,我们知道SOAP协议是定义了在WebServices之间传递消息的规范格式,在此基础上Services之间的消息交换将不再受到各种不同底层(传输层)的传输协议的影响,然而SOAP协议中并没有定义如何寻址一个WebServices,寻址是通过底层传输协议的连接定义的。早先时候基于大多数的WebServices都是通过Http协议访问这一前提下,采用同步request-response 的消息交换模式并不存在寻址的问题,因为http 协议的特性,回复消息直接通过http 请求本身的连接返回了。但是现实中SOA 要面临的场景要复杂得多,http已经 解决不了所有的问题:
(1)分离的消息发送者和回复的接收者;
换句话说:消息发送者和接收者的分离的情况可能包括连接的分离、时间的分离、协议的分离,例如:a、消息发送者不接收回复消息,而是指定了另外一个接收地址;b、消息的交换采用了无连接的协议;b、发送消息与回复消息采用不同的协议;c、消息需要经过数分钟、数小时甚至数天才处理完成产生回复。
(2)消息处理过程跨越多协议、多平台。
换句话说:消息处理过程是跨协议、跨平台的情形,例如,消息以 http 协议发送给服务 A ,被经过一定的处理后被以 smtp 发送给了属于另一平台的服务B,而在处理完成之后,回复消息要求发送给消息原始发送者指定的回复地址。
总之,针对特定的系统集成环境,每个系统开发者都会找到自己的解决方案。要把它们集成在一起,就需要一起协商出一套共同的规则用于描述消息的寻址信息,这些内容包括:发送给谁、回复给谁、以及采用什么方式。而这正是WS-Addressing 要解决的问题。在这样的环境下WS-Addressing是一些主要技术厂商(如Microsoft、Sun、BEA、IBM和SAP)协商的结果,并得到 W3C 组织的认可,并最终被 W3C 修订和确定为一个标准规范。
(2)WS-Addressing规范的消息格式
WS-Addressing 规范内容有专门的ws-addressing协议文件学习研究,我也是从英文版的手册中从中学习的,在这就不再重复其中的内容,只将我理解的部分简单的介绍下:
本质上,WS-Addressing 是 SOAP 消息头中如果描述寻址信息的一个规范。先看SOAP 消息头的结构非常有助于我们的理解。进而有助于我们填充probe函数中的相关信息。
1、SOAP 消息头的结构
我们先看线面soap Message的结构图:
其中:
MessageID :标识一个消息的 UUID 编号;
RelatesTo:回复的消息所应答的原始消息的MessageID;
To :消息要发往的 URI ;
ReferenceParameters :这是可选项,与To 一起构成对Endpoint Reference 的描述;
Action :定义了服务中处理消息方法,即WSDL中定义的操作的 Action;
From :发送消息的 URL;
ReplyTo:回复消息发往的 URL;通过该项可以指定一个不同于From 的地址,以指示服务将回复消息发往该地址;
FaultTo:消息处理失败时回复错误消息发往的URL ;
2、请求/回复消息的消息头对比
我们还需要了解的是ws-addressing请求和回复soapMessage之间的关系,这有助于我们理解他们之间存在的关系,能够跟好的理解程序。
总之,我们搞清楚这些关系了,就可以根据ws-addressing协议手册,结合gsoap生成的协议框架中定义的数据结构成员进行相关的赋值。
3、设备发现功能之探测函数的实现
服务端的开发相对来说比客户端稍微难一点,也就是给填充相关结构体的时候,需要一点一点的去查阅,验证各个结构中各个成员各自代表什么意思,以及对应的功能需要是那个接口实现,这是开发服务端最头疼的事情。
简而言之,这一部分主要是相关结构体成员的填充,而我们需要做的就是弄清我们要填充哪些结构体,如何填充这两个问题。
通过我这段时间的学习和对代码的分析,我总结出了两种解决办法:
第一种,通过抓包工具抓包海康设备来填充相关信息,这里就得要求我们得读懂soap信息,把soap中的信息映射到我们的程序中去。
第二种,就是仔细研读WS-Discovery文件和ONVIF Core Specification,并了解soap格式。
首先是了解消息头header和ProbeMatches中的内容,非常重要,可以参考这里http://www.w3.org/Submission/ws-addressing/ 最好详细的学习一下,里面的内容非常重要。
其次需要理解的是,其实当你看完ws-addressing后你会发现,骨架代码中的结构体和SOAP消息中的内容是一一对应的,
例如:
结构体osap->header对应SOAP消息的<SOAP-ENV:Header></SOAP-ENV:Header>中的内容,包含在header里的内容当然会包含在SOAP的header内。
例如:
结构体soap->header->wsa__RelatesTo对应的是<wsa:RelatesTo></wsa:RelatesTo>。
关与soap消息我就不多介绍了,下面两个图有助理解:
3、设备发现main函数代码的分析
1. int main()
2. {
3. int server_udp;
4.
5. int retval=0;
6. struct soap *soap_udp;
7. int fault_flag = 0;
8.
9. server_udp = create_server_socket_udp();
10. //创建udp套接字
11. bind_server_udp1(server_udp);
12. //绑定udp套接字
13. while(1){
14. soap_udp=soap_new();
15. soap_init1(soap_udp,SOAP_IO_UDP);
16. //定义并初始化新的环境变量
17. soap_udp->master = server_udp;
18. soap_udp->socket =server_udp;
19. //这两个部分需填充的是socket,分别关联到port和send and reciver
20. soap_udp->errmode = 0;
21. soap_udp->bind_flags = 1;
22. if(!soap_valid_socket(soap_bind(soap_udp, NULL, 3702,)))
23. {
24. soap_print_fault(soap_udp,stderr);
25. }
26. //判断是不是有效地socket
27. fprintf(stderr,"soap_servestarting..\n");
28. retval = soap_serve(soap_udp);
29. //阻塞在这里
30. fprintf(stderr,"retval=%d\n",retval);
31. if(retval &&!(fault_flag))
32. {
33. fault_flag = 1;
34. }
35. else if(!retval)
36. {
37. fault_flag = 0;
38. }
39. soap_destroy(soap_udp);
40. soap_end(soap_udp);
41. //清楚环境变量
42. soap_done(soap_udp);
43. free(soap_udp);
44. //释放环境变量
45. }
46. }
推荐参考的博客:打开
个人观点,有问题请斧正!!
转载请注明出处:http://blog.csdn.net/wang_zheng_kai
gsop入门学习笔记(二)---gsop编程简述以及discover的实现,布布扣,bubuko.com
gsop入门学习笔记(二)---gsop编程简述以及discover的实现
原文:http://blog.csdn.net/wang_zheng_kai/article/details/20699901