Tutoriel gsoap

r0d

Je vous propose ici un petit tutoriel pour créer rapidement un client et/ou un serveur gSoap stand-alone en C/C++.
Après une courte présentation, j'y détaille comment faire sous Windows et sous Linux. ♪

- Titre : Tutoriel gsoap

- Auteur : r0d

- Parution : 9 août 2009

- Mise à jour : 5 juillet 2010

- Licence : Copyright ® 2010 r0d. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Préliminaires

- I-A. Introduction

- I-B. Installation

- I-C. À propos de soapcpp2.exe

- I-D. À propos de wsdl2h.exe

- II. Tutoriel Windows

- II-A. À partir d'un en-tête

- II-A-1. Le serveur

- II-A-2. Le client

- II-A-3. Les sources

- II-B. À partir d'un fichier wsdl

- II-B-1. Le fichier WSDL

- II-B-1-a. Définitions

- II-B-1-b. Types

- II-B-1-c. Messages

- II-B-1-d. Port types

- II-B-1-e. Bindings

- II-B-1-f. Services

- II-B-2. Le serveur

- II-B-3. Le client

- II-B-4. Les sources

- II-C. Un mot sur les tests

- III. Notes pour Linux

- IV. Liens

I - Préliminaires

I-A - Introduction

SOAP est un protocole de communication. Il détermine le format des trames qui vont être envoyées du client au serveur (et vice-versa) afin que chacun puisse reconstituer les données envoyées.

gSoap, contrairement à ce qui est souvent écrit, n'est pas une bibliothèque. En réalité, c'est un compilateur: il prend un fichier (.h ou .wsdl) et crée les fichiers sources (.c/.cpp et .h) qui implémentent un serveur et/ou un client.
La partie émergée de gSoap, celle dont traite ce tutoriel, se compose de deux exécutables : soapcpp2.exe et wsdl2h.exe.

Il y a donc deux façons d'utiliser gSoap :

1. À partir d'un en-tête c/c++ (fichier .h) ;

2. À partir d'un fichier wsdl.

L'avantage du 1. c'est que c'est plus simple: il n'est pas nécessaire de connaitre la norme wsdl, et pour un développeur c/c++ c'est plus « intuitif » d'écrire un en-tête c/c++ qu'un wsdl.
L'avantage du 2. c'est qu'il existe des outils pour vérifier la validité du wsdl, créer un xsd correspondant, etc. le wsdl étant du xml, c'est également plus facile pour un développeur qui n'aime/connais pas le c/c++.

Dans les deux cas, le résultat est le même: un ensemble de fichiers .h et .c/.cpp qui, une fois compilés, donnent un serveur et/ou un client SOAP.
La nature du serveur (stand alone ou « attaché » à un serveur existant, version, etc.) dépend des paramètres de compilation passés aux compilateurs wsdl2h et soapcpp2.

Dans ce tutoriel, je ne traiterai que du serveur de type « stand alone », et je n'y aborde pas la problématique des proxies.

I-B - Installation

Tout d'abord il vous faut télécharger gSoap. Ce tutoriel est écrit et testé avec la version 2.7.14. Pour le télécharger, rendez-vous sur la page de téléchargement de gSoap sur sourceforge et choisissez la version que vous souhaitez.
Dézippez-le dans le dossier de votre choix, et c'est tout.

I-C - À propos de soapcpp2.exe

soapcpp2 est l'exécutable qui génère les fichiers sources d'un client et/ou d'un serveur SOAP à partir d'un en-tête (fichier .h).
Exemple d'utilisation :

soapcpp2.exe mon_client.h

Liste des principaux paramètres d'exécution

- -C ne génère que le code pour le client.

- -S ne génère que le code pour le serveur.

- -L ne génère pas les fichiers xxxLib (ces fichiers servent à créer notre client/serveur sous forme de bibliothèque).

- -c génère du code C (sinon, ça génère du C++).

- -d path spécifie l'endroit où sont générés les fichiers. Si ce paramètre n'est pas spécifié, les fichiers sont générés dans le répertoire courant.

I-D - À propos de wsdl2h.exe

WSDL est un protocole qui permet de définir un web service (la norme wsdl est décrite ici). Écrire un fichier wsdl correspond à définir précisément ce que va faire ce web service.
Le wsdl est une forme particulière de XML.

wsdl2h est l'exécutable qui génère l'en-tête d'un client et/ou d'un serveur SOAP à partir d'un fichier wsdl.
Exemple d'utilisation :

wsdl2h.exe my_cs.wsdl

Liste des principaux paramètres d'exécution

- -c génère du code C (sinon, ça génère du C++).

- -I path va chercher le(s) fichier(s) wsdl dans path.

- -o file spécifie le nom de l'en-tête généré.

- -s ne génère pas le code nécessaire à l'utilisation de la stl.

II - Tutoriel Windows

Afin de comprendre comment fonctionne gSoap, nous allons créer un serveur SOAP qui résout une opération simple, ainsi que son client. Cette opération simple, que j'appellerai op1, prend deux paramètres entiers a et b et effectue l'opération suivante : r = 2a + b. r étant le résultat, c'est-à-dire la valeur qui sera renvoyée au client par le serveur.

II-A - À partir d'un en-tête

Nous commencerons par écrire l'en-tête, qui définit juste la signature de notre opération. Nous pouvons l'appeler mon_cs.h (mon_client serveur) :

// mon_cs.h
int op1( int a, int b, int * r );

Attention, les fonctions gSoap ne peuvent retourner qu'un entier, qui est un code d'erreur. Le résultat doit donc être récupéré par un paramètre passé sous forme de pointeur ou de référence. Ici, le résultat est le troisième paramètre (r).

Ensuite, nous allons générer le client et le serveur. Pour ce faire, je conseille de procéder comme suit :
1. Copier l'exécutable (soapcpp2.exe) dans le même répertoire que votre en-tête ;
2. Créer un fichier batch (par exemple: mon_cs.bat), au même endroit, et d'y écrire la ligne de commande qui génère le client et/ou le serveur.

Voici le batch le plus simple que je vous propose :

soapcpp2.exe -L mon_cs.h
pause

Le -L c'est pour que gSoap ne génère pas les fichiers pour utiliser notre client/serveur comme une bibliothèque. Ce n'est pas nécessaire, mais cela évite des fichiers que nous n'utiliserons pas ici.
la ligne « pause » est importante: elle sert à ce que la fenêtre cmd ne se ferme pas immédiatement afin que nous puissions vérifier que la génération s'est bien déroulée, ou de voir, en cas de problème, les messages d'erreurs.

Si la génération s'est bien déroulée, les fichiers suivants ont été créés :
- soapC.cpp ;
- soapClient.cpp ;
- soapServer.cpp ;
- soapH.h ;
- soapStub.h.

Ces fichiers devront être compilés avec le reste de votre code (sauf soapClient.cpp pour le serveur et soapServer.cpp pour le client).

Il vous faudra également ajouter les fichiers stdsoap2.h et stdsoap2.cpp à votre projet. Ces deux fichiers se trouvent à la racine du dossier gSoap que vous avez téléchargé. Je vous conseille de copier ces deux fichiers dans le répertoire de votre projet.

II-A-1 - Le serveur

Un serveur gSoap est un web service. Il s'agit donc d'une boucle qui attend des trames sur un port donné. Et lorsqu'une trame est reçue, il la lit et regarde ce qu'il peut en faire. Il y a donc deux parties dans un serveur :
1. La boucle principale ;
2. La définition des opérations.

La boucle principale ressemblera à ceci :

SOAP_SOCKET s; // slave socket
struct soap soap; // structure soap
for ( ; ; )
{
s = soap_accept( &soap ); // reception d'une requete
std::cout << "connexion au client ok: slave socket = " << s << std::endl;
if ( !soap_valid_socket( s ) )
{
std::cout << "erreur: problème de socket" << std::endl << "appuyez sur une touche pour quitter";
return 1;
}

// execution de la requete. soap_serve() va appeler la fonction correspondante à la requete.
soap_serve( &soap );

// finalisation
soap_end( &soap );
}

Ensuite, les opérations seront de simples fonctions écrites en C++. Par exemple, notre op1() sera écrite comme suit :

int op1( struct soap *soap, int a, int b, int * r )
{
std::cout << "op1 appelé avec: a=" << a << ", b=" << b << std::endl;
*r = 2 * a + b;
return SOAP_OK;
}

II-A-2 - Le client

Un client SOAP est un programme qui envoie des requêtes à un serveur dont il connait l'adresse et le port d'accès.
Dans ce tutoriel, le client est, à l'image du serveur, minimal et ne sait faire qu'une opération (celle que j'appelle op1).
Le client est donc très simple. La seule ligne de code représentative est la suivante :

soap_call_op1( &soap, server, "", a, b, &result );

soap_call_op1 est une fonction générée par gSoap à partir de notre en-tête. Elle va récupérer les paramètres, créer la ou les trames correspondantes selon le protocole SOAP, et les envoyer.

Ce client s'utilise ainsi :
- ouvrez une invite de commande ;
- allez dans le répertoire où est généré l'exécutable du client ;
- entrez la ligne de commande: client a b (ou a et b sont des entiers).

II-A-3 - Les sources

Code source du client/serveur (avec fichiers projet et solution pour visual 2008 express ed.) :
sources

Ce dossier contient l'en-tête à partir duquel gSoap va générer le client et le serveur, ainsi que le petit script qui lance cette génération en utilisant l'exécutable soapcpp2.
Cet exemple est minimal et crée un client/serveur qui fonctionne en local.

II-B - À partir d'un fichier wsdl

Nous commencerons donc par écrire le fichier wsdl à partir duquel tout le reste va être généré.
La norme WSDL est assez complexe, car elle permet de définir entièrement un web service, aussi je ne vais pas m'y attarder ici et vous donner un exemple minimal. Pour approfondir, vous pouvez aller sur le site de W3C où la norme est décrite exhaustivement (ici).

II-B-1 - Le fichier WSDL

II-B-1-a - Définitions

Tout d'abord, il faut préciser des définitions qui vont servir à la génération du client/server. Pour ma part j'utilise toujours la même, à savoir :

<definitions name="mon_cs"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:ns="urn:my_cs"
xmlns:SOAP="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:MIME="http://schemas.xmlsoap.org/wsdl/mime/"
xmlns:DIME="http://schemas.xmlsoap.org/ws/2002/04/dime/wsdl/"
xmlns:WSDL="http://schemas.xmlsoap.org/wsdl/"
xmlns="http://schemas.xmlsoap.org/wsdl/">

Vous avez juste à remplacer « mon_cs » par le nom de votre propre client/serveur.

II-B-1-b - Types

Ensuite viennent les types dont vous aurez besoin. Par exemple, si vous utilisez une structure complexe pour envoyer ou recevoir des données, il faut la déclarer ici. Dans l'exemple que je donne ici, et dont vous trouverez le code source en fin de paragraphe, je n'utilise aucun type particulier, je n'ai donc rien déclaré ici. Pour plus de précision, je vous renvoie àla définition de la norme.

II-B-1-c - Messages

Ensuite viennent les définitions des messages. Un message se définit par une requête et une réponse. Dans la requête, il faut spécifier les paramètres (avec éventuellement des types définis dans la section précédente). Idem pour la réponse.
Par exemple, pour notre opération op1, qui prend deux entiers en entrée et qui renvoie un entier, ça donnera ceci :

<message name="op1Request">
<part name="a" type="xsd:int"/>
<part name="b" type="xsd:int"/>
</message>

<message name="op1Response">
<part name="r" type="xsd:int"/>
</message>

II-B-1-d - Port types

Après viennent les définitions des « port types ». Il s'agit de déclaration d'opérations virtuelle. C'est, en quelque sorte, une prédéclaration des opérations. Pour notre opération op1, nous aurons ceci :

<portType name="my_csPortType">
<operation name="op1">
<documentation>2a+b</documentation>
<input message="tns:op1Request"/>
<output message="tns:op1Response"/>
</operation>
</portType>

À noter que la balise <documentation> est optionnelle.

II-B-1-e - Bindings

Maintenant viennent les bindings. Les bindings font le lien entre les port types et les opérations.
Pour op1, nous aurons :

<binding name="my_cs" type="tns:my_csPortType">
<SOAP:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="op1">
<SOAP:operation style="rpc" soapAction=""/>
<input>
<SOAP:body use="encoded" namespace="urn:my_cs" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
</input>
<output>
<SOAP:body use="encoded" namespace="urn:my_cs" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
</output>
</operation>
</binding>

II-B-1-f - Services

Et enfin, on définit le service, dans lequel nous spécifions notamment l'adresse du serveur.

<service name="my_cs">
<documentation>test of gSoap</documentation>
<port name="my_cs" binding="tns:my_cs">
<SOAP:address location="http://127.0.0.1"/>
</port>
</service>

II-B-2 - Le serveur

Un serveur gSoap est un web service. Il s'agit donc d'une boucle qui attend des trames sur un port donné. Et lorsqu'une trame est reçue, il la lit et regarde ce qu'il peut en faire. Il y a donc 2 parties dans un serveur :
1. La boucle principale ;
2. La définition des opérations.

La boucle principale ressemblera à ceci :

SOAP_SOCKET s; // slave socket
struct soap soap; // structure soap
for ( ; ; )
{
s = soap_accept( &soap ); // reception d'une requete
std::cout << "connection au client ok: slave socket = " << s << std::endl;
if ( !soap_valid_socket( s ) )
{
std::cout << "erreur: problème de socket" << std::endl << "appuyez sur une touche pour quitter";
return 1;
}

// execution de la requete. soap_serve() va appeler la fonction correspondante à la requete.
soap_serve( &soap );

// finalisation
soap_end( &soap );
}

Ensuite, les opérations seront de simples fonctions écrites en C++. Par exemple, notre op1() sera écrite comme suit :

int ns1__op1( struct soap *soap, int a, int b, int & r )
{
std::cout << "op1 appelé avec: a=" << a << ", b=" << b << std::endl;
r = 2 * a + b;
return SOAP_OK;
}

II-B-3 - Le client

Un client SOAP est un programme qui envoie des requêtes à un serveur dont il connait l'adresse et le port d'accès.
Dans ce tutoriel, le client est, à l'image du serveur, minimal et ne sait faire qu'une opération (celle que j'appelle op1).
Le client est donc très simple. La seule ligne de code représentative est la suivante :

soap_call_op1( &soap, server, "", a, b, result );

soap_call_op1 est une fonction générée par gSoap à partir de notre en-tête. Elle va récupérer les paramètres, créer la ou les trames correspondantes selon le protocole SOAP, et les envoyer.

Ce client s'utilise ainsi :
- ouvrez une invite de commande ;
- allez dans le répertoire où est généré l'exécutable du client ;
- entrez la ligne de commande: client a b (ou a et b sont des entiers).

II-B-4 - Les sources

Code source du client/serveur (avec fichiers projet et solution pour visual 2008 express ed.):
sources.

Ce dossier contient les fichiers sources du client et du serveur, ainsi que le script qui lance la génération du code. Cette génération se fait en deux phases :
1. Génération de l'en-tête à partir du fichier wsdl ;
2. Génération du client et du serveur à partir de ces en-têtes.

Cet exemple est minimal et crée un client/serveur qui fonctionne en local.

II-C - Un mot sur les tests

Pour tester votre client et/ou votre serveur, je conseille de passer par un logiciel externe. En effet, si l'on se contente de tester notre propre client avec notre propre serveur, et vice-versa, on n'est nullement assuré que notre serveur fonctionnera avec d'autres clients SOAP et respectivement, que notre client fonctionnera avec d'autres serveurs SOAP.
Pour ma part, j'utilise soapUI, qui est gratuit et que vous pourrez trouver ici.

III - Notes pour Linux

gSoap fonctionne exactement de la même façon sous Windows et Linux.
Vous pouvez donc lire le tuto Windows et récupérer ses sources, ce sera pareil, sauf les petits batches de génération qu'il faudra traduire dans votre shell préféré.

IV - Liens

Commentez

Page de téléchargement de gSoap sur sourceforge
Doc officielle de gSoap
Norme WSDL