gsoap入门学习笔记(二)---gsoap编程简述以及discover的实现

这篇主要是对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_initsoap);

7.      soap_serversoap);

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,分别关联到portsend 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


  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值