zoukankan      html  css  js  c++  java
  • VC++ 实现soap服务端

    gSOAP是一个夸平台的,用于开发Web Service服务端和客户端的工具,在Windows、Linux、MAC OS和UNIX下使用C和C++语言编码,集合了SSL功能。

    下载地址:http://sourceforge.net/projects/gsoap2

    官方网站:http://genivia.com/Products/gsoap/index.html

    对于Windows平台下开发客户端,首先下载最新的gsoap_win32_2.7.6c.zip包,具体在以下地址:http://optusnet.dl.sourceforge.net/sourceforge/gsoap2/gsoap_win32_2.7.6c.zip

    首先查看gsoap的User's Guide,基本就能对gsoap有个全面的了解,通过阅读Sample里的例子程序深入。然后搜索网上其它一些文章,比如:
    gSOAP简单多线程服务器程序 http://blog.chinaunix.net/u1/55091/showart_430965.html
    纯c gSoap实现WebService            http://hi.baidu.com/2sky2sea/blog/item/40ec5555680279c1b745ae9b.html 


    接下来我结合自己的实践与理解,讲讲VC用gsoap下编写webService和客户端程序,有不对的地方还请大家指正,谢谢。
    我以网上出现的实现一个简单的加法函数为例,讲讲我在操作过程中遇到的问题。

    一 服务器端
    1.首先编写 add.h文件:
    1//gsoap ns service name: add
    2//gsoap ns service namespace: http://localhost/add.wsdl
    3//gsoap ns service location: http://localhost
    4//gsoap ns service executable: add.cgi
    5//gsoap ns service encoding: encoded
    6//gsoap ns schema namespace: urn:add
    7
    8int ns__add( int num1, int num2, int* sum );
    9

    2.用gsoap/bin目录下的soapcpp2.exe程序,生成一些文件。可以把soapcpp2.exe拷贝到一add.h目录下,用cmd执行soapcpp2.exe add.h就可以,在这个目录下会自动生成许多将来有用的文件,如add.namap,soapH.h,soapC.cpp,soapClient.cpp,soapServer.cpp等文件。soapcpp2.exe可以带参数执行,具体执行soapcpp2.exe -h查看。

    3.新建一个win32控制台工程,加入wsock32.lib库,将刚才生成的那些文件添加到工程中。然后编写webserver.cpp主程序:
    #include "add.h"
    #include "add.nsmap"

    int main(int argc, char* argv[])
    {
        
        int m, s; /* master and slave sockets */
        struct soap add_soap;
        soap_init(&add_soap);
        //soap_set_namespaces(&add_soap, add_namespaces);
        
        if (argc < 2)
        {
            printf("usage: %s <server_port>  ", argv[0]);
            exit(1);
        }

        else
        
            m = soap_bind(&add_soap, NULL, atoi(argv[1]), 100);
            if (m < 0)
            {
                soap_print_fault(&add_soap, stderr);
                exit(-1);
            }

            
            fprintf(stderr, "Socket connection successful: master socket = %d ", m);
            for ( ; ; )
            
                s = soap_accept(&add_soap); 
                if (s < 0)
                
                    soap_print_fault(&add_soap, stderr);
                    exit(-1);
                }

                fprintf(stderr, "Socket connection successful: slave socket = %d ", s);
                
                soap_serve(&add_soap);//该句说明该server的服务
                soap_end(&add_soap);
            }

        }

        return 0;
    }

    //server端的实现函数与add.h中声明的函数相同,但是多了一个当前的soap连接的参数
    int ns__add(struct soap *add_soap, int num1, int num2, int *sum)
    {
        *sum = num1 + num2;
        return 0;
    }


    4. 编译这个程序,会提示错误,将gsoap_win32目录下stdsoap2.cpp,stdsoap2.h文件加入工程,重新编译如果还有错误,可能是你将add.h生成的文件添加入工程出错的原因。实际上在编写server程序时,无须带Client的那些文件,还有带Lib的文件也无须添加到工程中。再重新编译应该就没有问题了,启动4567端口,在ie中输入localhost:4567,如果显示xml页面,说明程序已经启动。

    二 对应的客户端
    1。客户端程序代码如下:
    #include <stdio.h>
    #include <stdlib.h>
    #include "soapH.h"
    #include "add.nsmap"


    int add(const char* server, int num1, int num2, int *sum);

    int main(int argc, char **argv) 
    {
        int result = -1;
        char* server="http://localhost:4567";
        int num1 = 0;
        int num2 = 0;
        int sum = 0;
        if( argc < 3 )
        {
            printf("usage: %s num1 num2  ", argv[0]);
            exit(0);
        }

        
        num1 = atoi(argv[1]);
        num2 = atoi(argv[2]);
        
        result = add(server, num1, num2, &sum);
        if (result != 0)
        {
            printf("soap err,errcode = %d ", result);
        }

        else
        {
            printf("%d+%d=%d ", num1, num2, sum );
        }

        return 0;
    }


    int add( const char* server, int num1, int num2, int *sum )
    {
        struct soap add_soap;
        int result = 0;
        soap_init(&add_soap);
    //    soap_set_namespaces(&add_soap, add_namespaces);
        
        
    //该函数是客户端调用的主要函数,后面几个参数和add.h中声明的一样,前面多了3个参数,函数名是接口函数名ns__add前面加上soap_call_
        soap_call_ns__add( &add_soap, server, "", num1, num2, sum );
        if(add_soap.error)
        {
            printf("soap error:%d,%s,%s ", add_soap.error, *soap_faultcode(&add_soap), *soap_faultstring(&add_soap) );
            result = add_soap.error;
        }
     
        soap_end(&add_soap);
        soap_done(&add_soap);
        return result;
    }

    2.客户端程序既可以新建一个新的win32控制台程序,将刚才生成的nsmap,soapH.h,soapClient.h等文件加入工程,编译既可。我是直接在原先工程中加入一客户端代码,将webserver.cpp文件移除,并且将soapServer.cpp等server端需要的文件移除,将soapClient.cpp等client端需要的cpp添加到工程,编译既可。
    3.启动server程序,F5客户端程序,经测试正常。

    三 遇到的问题
    1.server端可以编译成CGI方式执行,而并不是绑定到某个端口,这种方式我没有实践。
    if (argc < 2) // no args: assume this is a CGI application 
       
          soap_serve(&soap); // serve request, one thread, CGI style 
          soap_destroy(&soap); // dealloc C++ data 
          soap_end(&soap); // dealloc data and clean up 
    }
    2.在编译服务器及客户端程序时一开始对add.h生成的文件添加到工程,经常出现问题,需要自己不调试。特别是链接时段,server/client要与其生成的文件相对应,server调用生成的soapserver.cpp,client调用生成的soapclient.cpp文件。
    3.多线程方式,在windows下建议用pthread_win32库,这里给出多线程下的例子。
    一 gSOAP需要的头文件:

    //gsoap ns service name: calc
    //gsoap ns service style: rpc
    //gsoap ns service encoding: encoded
    //gsoap ns service namespace: http://127.0.0.1:8089/calc.wsdl
    //gsoap ns service location: http://127.0.0.1:8089/cal
    //gsoap ns schema  namespace:    urn:calc
    int ns__add(double a, double b, double *result);
    int ns__sub(double a, double b, double *result);
    int ns__mul(double a, double b, double *result);
    int ns__div(double a, double b, double *result);
    int ns__pow(double a, double b, double *result);

    二 多线程服务器关键代码

    #include 
    #include  "calc.nsmap"
    #include  "soapH.h"

    /////////////////////////////////////////////////////////////////////////
    ///宏与全局变量的定义

    #define  BACKLOG (100)  
    #define  MAX_THR (10)   
    #define  MAX_QUEUE (1000)


    pthread_mutex_t queue_cs;                        //队列锁
    pthread_cond_t  queue_cv;                          //条件变量
    SOAP_SOCKET     queue[MAX_QUEUE];   //数组队列
    int                           head =0, tail =0;          //队列头队列尾初始化         
    //////////////////////////////////////////////////////////////////////////


    //////////////////////////////////////////////////////////////////////////

    void *      process_queue(void *);        //线程入口函数
    int         enqueue(SOAP_SOCKET);  //入队列函数
    SOAP_SOCKET dequeue(void);         //出队列函数

    //////////////////////////////////////////////////////////////////////////
    //线程入口函数
    void * process_queue(void * soap)
    {
      struct soap * tsoap = (struct soap *)soap;
      for(;;)
      {
            tsoap->socket = dequeue();
            if (!soap_valid_socket(tsoap->socket))
           {
             break;
            }

            soap_serve(tsoap);
            soap_destroy(tsoap);
            soap_end(tsoap);
      }

      return NULL;
    }


    //入队列操作
    int enqueue(SOAP_SOCKET sock)
    {
      int status = SOAP_OK;
      int next;
      pthread_mutex_lock(&queue_cs);
      next = tail +1;
      if (next >= MAX_QUEUE) 
        next = 0;
      if (next == head) 
          status = SOAP_EOM;
      else
      {
        queue[tail] =sock;
        tail = next;
      }

      pthread_cond_signal(&queue_cv);
      pthread_mutex_unlock(&queue_cs);
      return status;
    }


    //出队列操作
    SOAP_SOCKET dequeue()
    {
      SOAP_SOCKET sock;
      pthread_mutex_lock(&queue_cs);
       while (head == tail )
       {
              pthread_cond_wait(&queue_cv,&queue_cs);
       }

      sock = queue[head++];
      if (head >= MAX_QUEUE)
            {
        head =0;
      }

      pthread_mutex_unlock(&queue_cs);
      return sock;
    }



    //////////////////////////具体服务方法////////////////////////////////////////
    //加法的实现
    int ns__add(struct soap *soap, double a, double b, double *result)
    {
          *result = a + b;
          return SOAP_OK;
    }
     
    //减法的实现
    int ns__sub(struct soap *soap, double a, double b, double *result)

         *result = a - b;
         return SOAP_OK;
    }
     
    //乘法的实现
    int ns__mul(struct soap *soap, double a, double b, double *result)

         *result = a * b;
         return SOAP_OK;
    }
     
    //除法的实现
    int ns__div(struct soap *soap, double a, double b, double *result)

       if (b)
           *result = a / b;
       else
      {
             char *s = (char*)soap_malloc(soap, 1024);
             sprintf(s, "Can't">http://tempuri.org/">Can't divide %f by %f", a, b);
             return soap_sender_fault(soap, "Division by zero", s);
      }

      return SOAP_OK;
    }
     
    //乘方的实现
    int ns__pow(struct soap *soap, double a, double b, double *result)

      *result = pow(a, b);
      if (soap_errno == EDOM) /* soap_errno 和errorno类似, 但是和widnows兼容 */
      
        char *s = (char*)soap_malloc(soap, 1024);
        sprintf(s, "Can't take the power of %f to  %f", a, b);
        sprintf(s, "Can't">http://tempuri.org/">Can't take power of %f to %f", a, b);
        return soap_sender_fault(soap, "Power function domain error", s);
      }

      return SOAP_OK;
    }
     

    //////////////////////////////////////////////////////////////////////////////////////////////////////
    //主函数
    int main(int argc,char ** argv)
    {
      struct soap ServerSoap;
         //初始话运行时环境
        soap_init(&ServerSoap);
        //如果没有参数,当作CGI程序处理
        if (argc <2) 
        {       
               //CGI 风格服务请求,单线程
              soap_serve(&ServerSoap);
              //清除序列化的类的实例
             soap_destroy(&ServerSoap);
             //清除序列化的数据
            soap_end(&ServerSoap);     
       }
    else
       {
         struct soap * soap_thr[MAX_THR];
         pthread_t tid[MAX_THR];
         int i,port = atoi(argv[1]);
         SOAP_SOCKET m,s;
          //锁和条件变量初始化
         pthread_mutex_init(&queue_cs,NULL);
         pthread_cond_init(&queue_cv,NULL);
         //绑定服务端口
        m = soap_bind(&ServerSoap,NULL,port,BACKLOG);
        //循环直至服务套接字合法
        while (!soap_valid_socket(m))
       {
                    fprintf(stderr,"Bind port error! ");
                    m = soap_bind(&ServerSoap,NULL,port,BACKLOG);
        }

        fprintf(stderr,"socket connection successful %d ",m);
                    
         //生成服务线程
        for(i = 0; i <MAX_THR; i++)

       {
          soap_thr[i] = soap_copy(&ServerSoap);
          fprintf(stderr,"Starting thread %d ",i);
          pthread_create(&tid[i],NULL,(void*(*)(void*))process_queue,(void*)soap_thr[i]);
        }

                    
        for(;;)
        {
          //接受客户端的连接
          s = soap_accept(&ServerSoap);
          if (!soap_valid_socket(s)) 
          {
            if (ServerSoap.errnum) 
                                    {
              soap_print_fault(&ServerSoap,stderr);
              continue;
            }
    else
            {
              fprintf(stderr,"Server timed out ");
              break;
            }

          }

           //客户端的IP地址
          fprintf(stderr,"Accepted connection from IP= %d.%d.%d.%d socket = %d ",
                                   ((ServerSoap.ip)>>24)&&0xFF,((ServerSoap.ip)>>16)&0xFF,((ServerSoap.ip)>>8)&0xFF,(ServerSoap.ip)&0xFF,(ServerSoap.socket));
          //请求的套接字进入队列,如果队列已满则循环等待
           while(enqueue(s) == SOAP_EOM)
                    Sleep(1000);
        }

        //服务结束后的清理工作
        for(i = 0; i < MAX_THR; i++)
        {
          while (enqueue(SOAP_INVALID_SOCKET) == SOAP_EOM) 
           {
               Sleep(1000);
          }

        }

        for(i=0; i< MAX_THR; i++)
        {
          fprintf(stderr,"Waiting for thread %d to terminate ..",i);
          pthread_join(tid[i],NULL);
          fprintf(stderr,"terminated ");
          soap_done(soap_thr[i]);
          free(soap_thr[i]);
        }

        pthread_mutex_destroy(&queue_cs);
        pthread_cond_destroy(&queue_cv);
      }

        //分离运行时的环境
      soap_done(&ServerSoap);
      return 0;
    }

  • 相关阅读:
    技术领导要不要写代码?
    资深程序员告诉你:如何用五年时间攒够100万?
    mfc基于对话框的应用程序,如何设置初始对话框大小,移动控件位置
    zend studio,操作记录
    xampp怎么操作数据库mysql
    mysql-font的理解
    delphi 中配置文件的使用(*.ini)和TIniFile 用法
    delphi 创建服务,安装与卸载服务
    sublime Text的一些用法(emmet插件、)
    apache (web服务器) ->php->mysql,xampp与wamp比较,WAMP与WNMP有什么区别
  • 原文地址:https://www.cnblogs.com/hzcya1995/p/13318712.html
Copyright © 2011-2022 走看看