Calculator客戶端和服務器示例

概述:
此示例在C中實現了一個簡單的SOAP / XML API客戶端和服務器。

Web服務規範示例
計算器XML Web服務calc.h在gSOAP soapcpp2工具要處理的文件中指定。該calc.h規範定義了五個計算器方法add,sub,mul,div和pow:

//gsoap ns service name:            calc Simple calculator service described at https://www.genivia.com/dev.html
//gsoap ns service protocol:        SOAP
//gsoap ns service style:           rpc
//gsoap ns service encoding:        encoded
//gsoap ns service namespace:       http://websrv.cs.fsu.edu/~engelen/calc.wsdl
//gsoap ns service location:        http://websrv.cs.fsu.edu/~engelen/calcserver.cgi

//gsoap ns schema namespace:        urn:calc

//gsoap ns service method: add Sums two values
int ns__add(double a, double b, double *result);

//gsoap ns service method: sub Subtracts two values
int ns__sub(double a, double b, double *result);

//gsoap ns service method: mul Multiplies two values
int ns__mul(double a, double b, double *result);

//gsoap ns service method: div Divides two values
int ns__div(double a, double b, double *result);

//gsoap ns service method: pow Raises a to b
int ns__pow(double a, double b, double *result);

此示例calc.h文件是手動定義的。可以通過在一個或多個Web服務的WSDL上運行wsdl2h命令來自動獲取具有Web服務規範的這些特殊“頭文件”:

wsdl2h -c -o calc.h http://www.genivia.com/calc.wsdl

這是calc.h從指定的WSDL calc.wsdl文件生成的。

在運行wsdl2h之前,我們建議修改typemap.dat以將XML名稱空間前綴綁定到wsdl2h將使用的名稱空間URI。由於此服務使用URI“urn:calc”,並且我們希望將其綁定到ns前綴,因此我們應將以下行添加到typemap.dat:

ns = "urn:calc"

要從WSDL獲取名稱空間URI,請在WSDL上運行wsdl2h,然後從生成的頭文件中檢索它們(它們位於頂部)。然後在添加要與命名空間關聯的前綴名稱後重新運行wsdl2h命令typemap.dat。

關於Web服務規範語法的一些註釋
gSOAP“頭文件”中的Web服務規範定義了服務和數據綁定接口。該規範使用帶有指令和註釋的普通C / C ++語法。

C / C ++類型和成員的XML名稱空間限定是通過帶有雙下劃線的C / C ++標識符命名約定來完成的,就像ns__add將前綴綁定ns到add。也可以使用冒號ns:add(ns:用soapcpp2剝離)。
@ 用於聲明應該序列化爲XML屬性的struct和class成員。
數據成員的缺省值可以=直接在struct或class聲明中賦值。
XML元素和屬性出現約束minOccurs:maxOccurs在結構或類成員聲明的末尾聲明爲表單。
XML元素重複是STL容器或動態數組。動態數組是一對大小字段和數組指針成員,例如$int size; struct ns__employee_record *manages是size指向的長度數組manages。
XML Web服務操作被聲明爲函數,其中最後一個參數是指向結果值的指針或引用。使用結構或類返回多個值。
這些//gsoap指令用於綁定XML名稱空間並定義SOAP Web服務屬性:

//gsoap prefix service name:      <WSDLserviceName> <documentationText>
//gsoap prefix service style:     [rpc|document]
//gsoap prefix service encoding:  [literal|encoded]
//gsoap prefix service namespace: <WSDLnamespaceURI>
//gsoap prefix service location:  <WSDLserviceAddressLocationURI>

綁定Web服務操作屬性:

//gsoap prefix service method-style:         <methodName> [rpc|document]
//gsoap prefix service method-encoding:      <methodName> [literal|encoded]
//gsoap prefix service method-action:        <methodName> <actionString>
//gsoap prefix service method-documentation: <methodName> <documentation>

要定義類型架構屬性:

//gsoap prefix schema namespace:          <schemaNamespaceURI>
//gsoap prefix schema elementForm:        [qualified|unqualified]
//gsoap prefix schema attributeForm:      [qualified|unqualified]
//gsoap prefix schema documentation:      <documentationText>
//gsoap prefix schema type-documentation: <typeName> <documentationText>

這裏prefix是C / C ++名稱中使用的XML名稱空間前綴,例如nsin ns__add。

具有服務和數據綁定接口的Web服務規範由soapcpp2處理以生成服務和數據綁定實現代碼,請參閱下面的構建步驟。

實現客戶端應用程序
客戶端應用程序calcclient.c在命令行運行,並使用命令行參數調用計算器Web服務來添加,減去,乘,除或增加兩個浮點數的冪:

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

/* the Web service endpoint URL */
const char server[] = "http://websrv.cs.fsu.edu/~engelen/calcserver.cgi";

int main(int argc, char **argv)
{
  struct soap *soap = soap_new1(SOAP_XML_INDENT); /* new context */
  double a, b, result;
  if (argc < 4)
  {
    fprintf(stderr, "Usage: [add|sub|mul|div|pow] num num\n");
    exit(1);
  }
  a = strtod(argv[2], NULL);
  b = strtod(argv[3], NULL);
  switch (*argv[1])
  {
    case 'a':
      soap_call_ns__add(soap, server, "", a, b, &result);
      break;
    case 's':
      soap_call_ns__sub(soap, server, "", a, b, &result);
      break;
    case 'm':
      soap_call_ns__mul(soap, server, "", a, b, &result);
      break;
    case 'd':
      soap_call_ns__div(soap, server, "", a, b, &result);
      break;
    case 'p':
      soap_call_ns__pow(soap, server, "", a, b, &result);
      break;
    default:
      fprintf(stderr, "Unknown command\n");
      exit(1);
  }
  if (soap->error)
    soap_print_fault(soap, stderr);
  else
    printf("result = %g\n", result);
  soap_destroy(soap); /* delete deserialized objects */
  soap_end(soap);     /* delete heap and temp data */
  soap_free(soap);    /* we're done with the context */
  return 0;
}

請參閱有關如何使用HTTP代理和HTTPS 的教程。

構建客戶端應用程序的步驟
爲客戶端生成服務和數據綁定接口:

soapcpp2 -c -r -CL calc.h

其中option -c生成C代碼,option -r生成報告,option只-CL生成客戶端而沒有(未使用的)lib文件。

這會生成多個文件,包括:

soapStub.h 沒有註釋的普通C / C ++頭文件語法規範的副本。
soapH.h 聲明XML序列化程序。
soapC.c 實現XML序列化程序。
soapClient.c 實現客戶端XML服務API。
calc.nsmap XML命名空間綁定表到#include
soapReadme.md 服務和數據綁定接口詳細信息。

要構建示例客戶端應用程序,我們還需要stdsoap2.h和stdsoap2.c:

gcc -o calcclient calcclient.c stdsoap2.c soapC.c soapClient.c

實現CGI服務器應用程序
CGI服務器應用程序calcserver.c實現服務操作並接受stdin soap_serve(soap)上的請求,將請求分派給這五個服務操作之一:

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

int main()
{
  struct soap *soap = soap_new(); /* new context */
  soap_serve(soap);   /* serve CGI request */
  soap_destroy(soap); /* delete deserialized objects */
  soap_end(soap);     /* delete heap and temp data */
  soap_free(soap);    /* we're done with the context */
  return 0;
} 
/* service operation function implementation */
int ns__add(struct soap *soap, double a, double b, double *result)
{
  *result = a + b;
  return SOAP_OK;
} 
/* service operation function implementation */
int ns__sub(struct soap *soap, double a, double b, double *result)
{
  *result = a - b;
  return SOAP_OK;
} 
/* service operation function implementation */
int ns__mul(struct soap *soap, double a, double b, double *result)
{
  *result = a * b;
  return SOAP_OK;
} 
/* service operation function implementation */
int ns__div(struct soap *soap, double a, double b, double *result)
{
  if (b)
    *result = a / b;
  else
    return soap_sender_fault(soap, "Division by zero", NULL);
  return SOAP_OK;
} 
/* service operation function implementation */
int ns__pow(struct soap *soap, double a, double b, double *result)
{
  *result = pow(a, b);
  if (soap_errno == EDOM) /* soap_errno is like errno, but portable */
    return soap_sender_fault(soap, "Power function domain error", NULL);
  return SOAP_OK;
}

使用下面進一步討論的構建步驟構建服務應用程序後,將CGI應用程序安裝在Web服務器的cgi-bin文件夾中。

實現獨立的服務器應用程序
此示例顯示了一個獨立的迭代服務器,它接受主機端口上的傳入請求。該程序與CGI服務相同,除了通過循環中的套接字調度服務請求:

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

int port = 8080;

int main()
{
  SOAP_SOCKET m;                               /* master socket */
  struct soap soap = soap_new();               /* new context */
  soap->send_timeout = soap->recv_timeout = 5; /* 5 sec socket idle timeout */
  soap->transfer_timeout = 30;                 /* 30 sec message transfer timeout */
  m = soap_bind(soap, NULL, port), 1);         /* backlog=1 for iterative servers, multi-threaded services should use 100 */
  if (soap_valid_socket(m))
  {
    while (soap_valid_socket(soap_accept(soap)))
    {
      soap_serve(soap);   /* serve request */
      soap_destroy(soap); /* delete deserialized objects */
      soap_end(soap);     /* delete heap and temp data */
    }
  }
  soap_print_fault(soap, stderr);
  soap_destroy(soap); /* delete deserialized objects */
  soap_end(soap);     /* delete heap and temp data */
  soap_free(soap);    /* we're done with the context */
  return 0;
}

注意:只要接受失敗,此服務器就會退出。最好是在發生短期連接問題時添加一些邏輯以繼續。

爲了提高性能,我們應該實現一個多線程服務器,它可以同時處理請求,並且在服務操作變得耗時時不會阻止其他客戶端請求:

#include "soapH.h"
#include "calc.nsmap"
#include "plugin/threads.h"

int port = 8080;

void *process_request(void *arg)
{
  struct soap *soap = (struct soap*)arg;
  THREAD_DETACH(THREAD_ID);
  if (soap)
  {
    soap_serve(soap);
    soap_destroy(soap);
    soap_end(soap);
    soap_free(soap);
  }
  return NULL;
}

int main()
{
  SOAP_SOCKET m;                                    /* master socket */
  struct soap *soap = soap_new1(SOAP_IO_KEEPALIVE); /* new context with HTTP keep-alive enabled */
  soap->send_timeout = soap->recv_timeout = 5;      /* 5 sec socket idle timeout */
  soap->transfer_timeout = 30;                      /* 30 sec message transfer timeout */
  m = soap_bind(soap, NULL, port), 100);
  if (soap_valid_socket(m))
  {
    while (soap_valid_socket(soap_accept(soap)))
    {
      THREAD_TYPE tid;
      void *arg = (void*)soap_copy(soap);
      /* use updated THREAD_CREATE from plugin/threads.h https://www.genivia.com/files/threads.zip */
      if (arg)
        while (THREAD_CREATE(&tid, (void*(*)(void*))process_request, arg))
          sleep(1);
    }
  }
  soap_print_fault(soap, stderr);
  soap_destroy(soap); /* delete deserialized objects */
  soap_end(soap);     /* delete heap and temp data */
  soap_free(soap);    /* we're done with the context */
  return 0;
}

此服務器對可生成的線程數沒有限制,因此您可能希望保持受互斥鎖保護的計數器不超過最大值。此外,使用線程池可以提高整體性能。最好是使用ISAPI和Apache模塊來開發gSOAP用戶指南中建議的強大而安全的服務。

請參閱有關如何使用線程池,啓用HTTPS以及如何強化獨立服務的教程。
請參閱有關如何使用ISAPI和Apache模塊開發服務的文檔。

構建服務器應用程序的步驟
使用以下命令爲服務器端生成服務和數據綁定接口:

soapcpp2 -c -r -SL calc.h

其中option -c生成C代碼,option -r生成報告,option只-SL生成服務器端而沒有(未使用的)lib文件。

這會生成多個文件,包括:

soapStub.h 沒有註釋的普通C / C ++頭文件語法規範的副本。
soapH.h 聲明XML序列化程序。
soapC.c 實現XML序列化程序。
soapServer.c 實現服務器端XML服務API。
calc.nsmap XML命名空間綁定表到#include
soapReadme.md 服務和數據綁定接口詳細信息。

要構建示例服務器應用程序,我們還需要stdsoap2.h和stdsoap2.c:

gcc -o calcserver calcserver.c stdsoap2.c soapC.c soapServer.c

運行示例
./calcclient add 2 3
result = 5
要在端口8080上對獨立服務器運行該示例,請更改server[] = “http://localhost:8080”。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章