UniComCtrl::Développement

Un article de OpenIHS.org.

Sommaire

[modifier] Structure de la bibliothèque

  • LibUniComCl :
    • Fonctionnalités générales :
      • Object : Information sur un objet.
      • Data : Structure de donnée envoyée sur le réseau.
      • Socket : Communication réseau.
      • IO : Gestion entrée/sortie et Logs.
      • Regex : Gestion d'expressions rationnelles.
      • ReqToolKit : Une boite à outils pour effectuer un analyseur de requêtes.
    • Configuration :
      • Settings : Gestion de configuration.
      • SettingsFile  : Gestion de configuration de type Unix, INI et miste.
      • SettingsReg  : Gestion de configuration de type registre Windows.
    • Mutex et signaux :
      • SigObject : Objets (dés)activable (interruptibles).
      • Mutex : Mutex (pthread_mutex).
      • Condition : Signaux (pthread_cond).
      • RWLock : Verrous en lecture/écriture (pthread_rwlock).
      • Semaphore : Sémaphores.
    • Gestion des taches :
      • Thread : Gestion d'une tache.
      • ThreadSock : Tache s'occupant de la communication sur le réseau.
      • ThreadSockTmpl : Ajout de fonctionnalités à la classe ThreadSock pour communiquer entre plusieurs applications.
      • UniComCl : Tache cliente pour dialoguer entre un client et un plugin ou avec le serveur.
    • Exceptions :
      • Exception : Exception générique.
      • DisabledException : Désactivation.
      • FatalErrorException : Erreur fatale.
      • TimeoutException : Temps écoulé.
      • IOException : Entrée/sortie.
        • IOErrorException : Erreur (Entrée/sortie).
        • ErrorMessageException : Message d'erreur (Entrée/sortie).
        • ConnectionErrorException : Erreur de connexion (Entrée/sortie).
        • ConnectionClosedException : Connexion fermée (Entrée/sortie).
        • TemporarilyUnavailableException : Service temporairement indisponible (Entrée/sortie).
        • PeerDisconnectionException : Évènement de déconnexion (Entrée/sortie).
  • LibUniComDB :
    • DB : Client de base de donnée.
    • DBMySQL : Client MySQL.
    • DBPostgreSQL : Client PostgreSQL.
    • DBSQLite3 : Client SQLite3.
    • DBException : Exception (base de donnée).
      • NoConnectedDBException : Non connecté.
      • SQLErrorDBException : Erreur SQL.
      • OutOfRangeDBException : Indice en dehors des limites.
      • ResultEndDBException : Fin du résultat.
      • FatalErrorDBException : Erreur fatale.
  • LibQTUniComCl :
    • QUniComCl : Tache cliente pour dialoguer entre un client et un plugin ou avec le serveur (version QT4).

[modifier] Documentation complète

Vous trouverez une documentation plus complète ici.

[modifier] Quelques objets et fonctions statiques

Ceci est une description des méthodes et objets statiques (globaux).

[modifier] IO

Cette classe s'occupe des entrées/sorties. Des objets en statique sont instanciés automatiquement au chargement de la bibliothèque libunicomcl par votre programme.

Il y a deux façon d'écrire sur la(les) sortie(s) programmées :

[modifier] Utilisation des flux

Il y a en tout 8 flux de sorties :

outn et soutn 
Sortie standard.
outd et soutd 
Sortie de débogage.
oute et soute 
Sortie d'erreur.
outse et soutse 
Sortie d'erreur système.

Les flux de sortie préfixé par 's' (pour statique) doivent être utilisés de préférence dans une fonction ou une méthode statique d'une classe. Quant aux autres il doivent être utilisés dans une méthode non statique d'une classe qui hérite de Object.

Chaque écriture sur un des flux doit impérativement être clôturée par l'objet endIO ou endlIO (qui rajoute un \n à la fin). Exemple :

outn << "Message à écrire" << endlIO;

En réalité quand vous utilisez ces flux il y a une macro du même nom qui remplace par exemple outd par outd.beginLine(this) qui commence par écrire une entête composée du numéro du thread suivit du nom de la classe fourni par l'appel de la méthode this->className(), ce qui permet de faciliter le débogage. Pour les objets préfixés par un 's', la macro est quasiment la même à une différence prêt, par exemple elle replacera soutd par outd.beginLine(0). Pour plus de renseignements sur les possibilités et résultats obtenus, rendez-vous ici

[modifier] Utilisation des méthodes de la classe Object

Il y a dans la classe Object 8 méthodes qui correspondent respectivement aux 8 flux décris ci-dessus.

Donc un appel à la méthode printn("Message\n") est équivalent à :

outn << "Message\n" << endIO;

[modifier] Paramétrage des E/S

  • niveau IO::getLevel() et IO::setLevel(nouveau niveau) :
Permet de définir le niveau de verbosité du programme.
  • booléen IO::getSyslog() et IO::setSyslog(nouveau mode) :
Permet d'(dé)activer la sortie système (SysLog).
  • booléen IO::getLog() et IO::setLog(nouveau mode) :
Permet d'(dé)activer la sortie dans le fichier journal.
  • chaîne de carractère IO::getLogPath() et IO::setLogPath(nouveau chemin) :
Permet de définir le chemin du fichier journal.

[modifier] Socket

Cette classe gère la communication par socket.

  • Socket::init()
L'appel de cette fonction est indispensable avant toute connexion sous Windows étant directement lié au WSAStartup(). Sous linux elle n'a donc aucun effet sauf si vous utilisez une connexion en SSL/TLS.
  • Socket::sockList
Liste des objets créés.
  • Socket::mutSockList
Mutex associé à la liste Socket::sockList.
  • Socket::getTimer (temps)
Donne le timeout utilisé pour la fonction select().
  • Socket::setTimer (nouveau temps)
Réglage du timeout utilisé pour la fonction select().

[modifier] Thread

Cette classe s'occupe de la gestion des taches. A chaque création de tache, celle-ci est référencée dans un tableau associatif Thread::list.
  • Thread::list
C'est un tableau associatif statique et privée de type "map" (map<pthread_t,adress>) dont la clé est l'id du thread et la valeur est une adresse d'objet Thread.
  • Thread::mutThreadList
Mutex associé à la liste Thread::list.

[modifier] Mode de transmission

Comme vous avez pu le constater dans le manuel de présentation, il y a deux mode de transmission : par requête bloquante où on attend la réponse et par transmission de donnée de façon non bloquante (fonctionnement évènementiel).

Toutes les données reçues sont bufferisées ou stockées de façon temporaire dans la classe cliente à l'exception des réponses aux requêtes synchrones envoyées avec la méthode query() par exemple qui, elles, sont stockées dans un objet Result passé en paramètre.

L'intégrité des données stockées dans la classe cliente est garantie de deux façon :

Automatiquement 
Elles sont garanties le temps de l'appel de la méthode onDataIn(), après quoi elle sont susceptible d'être écrasée pas les données suivantes.
Manuellement 
Elles sont garanties jusqu'à l'appel de la méthode relaseData(), après quoi elle sont susceptible d'être écrasée pas les données suivantes.

Le mode de contrôle d'intégrité peut être contrôlé individuellement pour chaque client avec la méthode setBufferedDataIntegrityMode(nouveau mode).

[modifier] Utilisation de la classe UniComCl et QUniComCl

Pour programmer un client, il suffit de d'écrire une nouvelle classe qui hérite de la classe UniComCl ou QUniComCl.

Il faut au minimum redéfinir la méthode void onDataIn(Data::Type type) (uniquement pour UniComCl) quitte à la laisser vide.

Exemple (UniComCl) :

  1. class Client : public UniComCl
  2. {
  3. public:
  4.     Client(const string& server): UniComCl(server){}
  5.     ~Client(){}
  6.     void onDataIn(Data::Type type){}
  7.     virtual string className(){
  8.         return "Client";
  9.     }
  10. };

[modifier] Format de l'adresse

L'adresse sert à définir le nom d'hôte, le support et aussi le moyen de connexion.

Syntaxe de l'adresse : [(unix|ssl|http|https)://][hostname][:port_number][path]

Expression rationnelle qui sert à décoder l'adresse : ((unix|ssl|http|https)://)?([^:/]+)?(:([0-9]+))?(/.*)?

L'adresse est décomposée en quatre partie :

Le préfix 
Sous la forme prefix://, il sert à identifier le mode de connexion, à savoir en ssl/tls ou sans, via un relai http ou pas.
Le nom d'hôte 
Sert à renseigner le nom d'hôte. Si le préfixe est unix cette partie est ignorée.
Le numéro de port 
Sous la forme numéro:, il sert à renseigner le numéro de port. Si le préfixe est unix cette partie est ignorée. Si cette partie n'est pas renseignée le numéro de port sera 3693 (pas de préfixe), 80 (préfixe http), 443 (préfixe https).
Le chemin 
Set à renseigner le chemin de la socket UNIX si le préfix est unix ou le chemin du module (apache) UCFwd si le préfix est http ou https, dans le reste des cas, il sera ignoré.

[modifier] Exemples de connexion

[modifier] Socket UNIX

  • Connexion sur "/tmp/unix.sock" :
  1. Client unClient("unix:///tmp/unix.sock");
  2. Client unClient("/tmp/unix.sock");
  • Connexion sur "/tmp/unix.sock" en mode SSL/TLS :
  1. Client unClient("ssl:///tmp/unix.sock");

[modifier] Socket TCP/IP

  • Connexion à localhost sur le port 1234 :
  1. Client unClient("localhost:1234");
  • Connexion à localhost sur le port 3693 (port par défaut) :
  1. Client unClient("localhost");
  • Connexion à localhost sur le port 1234 en mode SSL/TLS :
  1. Client unClient("ssl://localhost:1234");
  • Connexion à localhost sur le port 3693 (port par défaut) en mode SSL/TLS :
  1. Client unClient("ssl://localhost");

[modifier] Socket TCP/IP via un relai HTTP/HTTPS

  • Connexion à localhost en http (port 80) avec le chemin du module UCFwd "/unicomctrl" :
  1. Client unClient("http://localhost/unicomctrl");
  • Connexion à localhost en http (port 8080) avec le chemin du module UCFwd "/unicomctrl" :
  1. Client unClient("http://localhost:8080/unicomctrl");
  • Connexion à localhost en https (port 443) avec le chemin du module UCFwd "/unicomctrl" :
  1. Client unClient("https://localhost/unicomctrl");
  • Connexion à localhost en https (port 9999) avec le chemin du module UCFwd "/unicomctrl" :
  1. Client unClient("https://localhost:9999/unicomctrl");

[modifier] Exemple

Pour concrétiser, rien de mieux qu'un petit exemple !

Pour cet exemple, le client se connecte à un plugin qui se contente de faire l'écho des requête du client.

Vous constaterez que l'utilisation de la bibliothèque est assez simple.

[modifier] Client (Processus)

[modifier] Compilation :

g++ client.cpp -Wall -o client -lunicomcl

[modifier] Contenu du fichier client.cpp :

  1. #include <unicomctrl/unicomcl.h>
  2. #include <unicomctrl/exception.h>
  3. #include <iostream>
  4. #include <string>
  5.  
  6. using namespace unicomctrl;
  7. using namespace std;
  8.  
  9. class Client : public UniComCl
  10. {
  11. public:
  12.     Client(const string& host): UniComCl(host){}
  13.     ~Client(){}
  14.     void onDataIn(Data::Type type){}
  15.     virtual string className(){
  16.         return "Client";
  17.     }
  18. };
  19.  
  20. int main(int argc, char **argv) {
  21.     //Initialisation
  22.     Socket::init();
  23.     Client *thread;
  24.     //Connexion
  25.     if(argc==2){
  26.         try{
  27.             thread=new Client(string(argv[1]));
  28.         }catch(Exception e){
  29.             cout << "Exception: " << e.getMsg() << endl;
  30.             exit(EXIT_FAILURE);
  31.         }
  32.     }else{
  33.         cout << "Use: " << argv[0] << " [(unix|ssl|http|https)://][<hostname>][:<port_number>][<path>]" << endl;
  34.         exit(1);
  35.     }
  36.     thread->start();
  37.     //Saisie d'information
  38.     string request,plugin,user,pass;
  39.     cout << "User: ";
  40.     getline(cin,user);
  41.     cout << "Password: ";
  42.     getline(cin,pass);
  43.     cout << "Plugin name: ";
  44.     getline(cin,plugin);
  45.     try{
  46.         //Identification et utilisation du plugin
  47.         thread->query(NULL,"connect '"+user+"' '"+pass+"'");
  48.         thread->query(NULL,"use '"+plugin+"'");
  49.         Client::Result result;
  50.         while(true){
  51.             cout << "Request: ";
  52.             getline(cin,request);
  53.             try{
  54.                 thread->query(&result,request,plugin);
  55.                 cout << "Answer : " << result.getMessage() << endl;
  56.             }catch(Exception e){
  57.                 if(e.getNo()<500 && e.getNo()>504)
  58.                     throw e;
  59.                 cout << "Error : " << e.getMsg() << endl;
  60.             }
  61.         }
  62.     }catch(Exception e){
  63.         cout << "STOP: " << e.getMsg() << endl;
  64.     }
  65.     thread->stop();
  66.     thread->join();
  67.     delete thread;
  68.     return 0;
  69. }

[modifier] Plugin (Processus)

[modifier] Compilation :

g++ -O2 -D_REENTRANT -Wall pluginpus.cpp -o pluginpus -lunicomcl

[modifier] Contenu du fichier pluginpus.cpp :

  1. #include <unicomctrl/unicomcl.h>
  2. #include <unicomctrl/settingsreg.h>
  3. #include <unicomctrl/settingsfile.h>
  4. #include <unicomctrl/exception.h>
  5. #include <string>
  6. #include <signal.h>
  7.  
  8. using namespace unicomctrl;
  9. using namespace std;
  10.  
  11. class PluginPus : public UniComCl
  12. {
  13. public:
  14.     PluginPus(const string& server): UniComCl(server){}
  15.     ~PluginPus(){}
  16.     /**
  17.      * Action à effectuer quand une donnée arrive de façon évènementielle.
  18.      * Il s'agit ici des requêtes reçues.
  19.      */
  20.     void onDataIn(Data::Type type){
  21.         switch(type){
  22.             case Data::REQUEST:
  23.                 sendMessage("Echo : "+getMessage(),getPeerName(),isSyncMode());
  24.                 break;
  25.             default:
  26.                 break;
  27.         }
  28.     }
  29.     virtual string className() const{
  30.         return "PluginPus";
  31.     }
  32. };
  33.  
  34. PluginPus *thread;
  35.  
  36. void quit(int sig){
  37.     //Arrêt du plugin.
  38.     thread->stop();
  39. }
  40.  
  41. int main(int argc, char **argv) {
  42.     //Initialisation.
  43.     Socket::init();
  44.     Settings *config=0;
  45.     try{
  46. #ifdef WIN32
  47.         config=new SettingsReg("Software\\UniComPl\\pluginpus",SettingsReg::REG_LM);
  48. #else
  49.         config=new SettingsFile("/etc/unicomctrl/pluginpus.conf");
  50. #endif
  51.         //Récupération des informations pour se connecter.
  52.         thread=new PluginPus(config->getValue("server"));
  53.        
  54.         //Paramétrage de l'interruption.
  55.         signal(SIGTERM,quit);
  56.         signal(SIGINT,quit);
  57.    
  58.         //Démarrage du client.
  59.         thread->start();
  60.         thread->query(NULL,"connect '"+config->getValue("user")+"' '"+config->getValue("passwd")+"'");
  61.         thread->query(NULL,"insert '"+config->getValue("name")+"'");
  62.        
  63.         //Arrêt et destruction du client.
  64.         thread->join();
  65.         delete thread;
  66.     }catch(Exception e){
  67.         soute << e.getMsg()+"\n" << endIO;
  68.         exit(EXIT_FAILURE);
  69.     }
  70.     delete config;
  71.     return 0;
  72. }

[modifier] Plugin (Bibliothèque dynamique) ###EXPERIMENTAL###

Cette sorte de plugin dépend d'un lanceur de plugin (cf projet UniComPl) qui les lance à l'aide d'un chargement dynamique (sous Linux : dlopen(),dlsym(), etc...).

[modifier] Compilation :

g++ -O2 -D_REENTRANT -Wall -fpic -fPIC -shared pluginlib.cpp -o pluginlib.so -lunicomcl

[modifier] Contenu du fichier pluginlib.cpp :

  1. #include <unicomctrl/unicomcl.h>
  2. #include <unicomctrl/settingsfile.h>
  3. #include <unicomctrl/settingsreg.h>
  4. #include <unicomctrl/exception.h>
  5. #include <string>
  6. #include <signal.h>
  7.  
  8. using namespace unicomctrl;
  9. using namespace std;
  10.  
  11. class PluginLib : public UniComCl
  12. {
  13. public:
  14.     PluginLib(const string& server): UniComCl(server){}
  15.     ~PluginLib(){}
  16.     /**
  17.      * Action à effectuer quand une donnée arrive de façon évènementielle.
  18.      * Il s'agit ici des requêtes reçues.
  19.      */
  20.     void onDataIn(Data::Type type){
  21.         switch(type){
  22.             case Data::REQUEST:
  23.                 sendMessage("Echo : "+getMessage(),getPeerName(),isSyncMode()); // Fait un écho de la requête
  24.                 break;
  25.             default:
  26.                 break;
  27.         }
  28.     }
  29.     virtual string className() const{
  30.         return "PluginLib";
  31.     }
  32. };
  33.  
  34. PluginLib *thread;
  35.  
  36. extern "C"
  37. {
  38.     /**
  39.      * Cette fonction est appelée au démarrage du plugin.
  40.      */
  41.     void plugin_start(Settings *config, const string &name) {
  42.         //Récupération des informations pour se connecter.
  43.         //On ignore volontairement les exeptions qui seront retansmise au lanceur.
  44.         thread=new PluginLib(config->getValue("server"));
  45.        
  46.         try{
  47.             //Démarrage du client.
  48.             thread->start();
  49.            
  50.             //Identification et insertion du plugin auprès du serveur.
  51.             thread->query(NULL,"connect '"+config->getValue(name+"_user")+"' '"+config->getValue(name+"_passwd")+"'");
  52.             thread->query(NULL,"insert '"+config->getValue(name+"_name")+"'");
  53.         }catch(Exception e){
  54.             thread->stop();
  55.             thread->join();
  56.             delete thread;
  57.             //On transmet les exeptions au lanceur.
  58.             throw;
  59.         }
  60.        
  61.         //Démarrage réussi !
  62.     }
  63.     /**
  64.      * Cette fonction est appelée a l'arrêt du plugin.
  65.      */
  66.     void plugin_stop(){
  67.         //Arrêt et destruction du client.
  68.         thread->stop();
  69.         thread->join();
  70.         delete thread;
  71.     }
  72. }
Compte svn/dav & co
Divers
Administration