Edit du 24 novembre 2012 : Ajout de la partie sur les sockets sous linux !
Mise à jour du GitHub, tout est expliqué dessus (voir fin de l'article) !
Dans ce paper, qui me permettra par la même occasion de vous présenter une lib très simple que j'ai codé pour faire des requetes web en C++, nous verrons comment créer une bibliothèque partagée (shared library).
Je suis sur Gnu/Linux et je ne developperais que cette aspect car c'est le seul que je connaisse.
Déjà, qu'est ce qu'une "Shared Library" ?
- Ce sont des bibliothèque qui sont chargés par le programme lors de son lancement, en d'autres termes, un programme nécéssitant une bibliothèque de ce type ne pourra fonctionner que si elle est installée sur le système.
- Ainsi ces bibliothèques sont idépendantes du programme, ce qui rend l'éxécutable plus léger (contrairement à une compilation statique par exemple).
Comment on crée une "Shared Library" ?
Nous verrons d'abord comment faire en utilisant g++ pour compiler une bibliothèque codée en C++, puis nous verrons comment faire pour en compiler une en C avec gcc (en réalité les commandes sont EXACTEMENT les mêmes).
La particularité lorsque l'on commence à coder une bibliothèque pour le système c'est qu'il n'y a pas de fonction main() mais uniquement des fonctions qui seront ensuite appellées, il va donc nous falloir deux fichiers, que nous
appellerons "simpleweb.h" et "simpleweb.cpp", un qui est le header et l'autre la source.
Attaquons nous d'abord au header (.h)
Les lignes suivante nous permettrons
1 2 size_t download(char *ptr, size_t size, size_t nmemb, std::string *stream); // Fonction de callback pour libCurl 3 int urlRetrieve(std::string url, std::string path); // Télécharge un fichier distant 4 std::string read(std::string url); // Retourne le code source d'une page web 5 std::string sendPost(std::map<std::string,std::string>& data, std::string url); // Envoi des données en POST à une page et retourne la source de la page
Mais pour pouvoir utiliser les différents types de données et prototypes suivants, nous devons également déclarer les lib que l'on doit inclure :
1 2#ifndef SIMPLEWEB_H 3#define SIMPLEWEB_H 4 #define CURL_STATICLIB 5 #include <iostream> 6 #include <fstream> 7 #include <string> 8 #include <curl/curl.h> 9 #include <curl/types.h> 10 #include <curl/easy.h> 11 #include <map> 12 size_t download(char *ptr, size_t size, size_t nmemb, std::string *stream); 13 int urlRetrieve(std::string url, std::string path); 14 std::string read(std::string url); 15 std::string sendPost(std::map<std::string,std::string>& data, std::string url); 16#endif
Voici ensuite le contenu des fonctions (utilisant libCurl), j'ai essayé de commenter au maximum mais si des éléments vous échappent n'hésitez pas à me demander des explications :
1 2size_t download(char *ptr, size_t size, size_t nmemb, std::string *stream) { // Fonction Callback appellée par CURLOPT_WRITEFUNCTION 3 if(stream != NULL){ // Si le pointeur est correct, 4 stream->append(ptr, size*nmemb); // On ajoute les données recu au string 5 return size*nmemb; // On retourne la taille des données 6 } 7 return 0; 8} 9 10int urlRetrieve(std::string url, std::string path){ // Télécharge un fichier distant 11 std::ofstream file(path.c_str(), std::ios::in | std::ios::trunc); // Fichier crée 12 CURL *curl; 13 std::string reponse=""; 14 curl = curl_easy_init(); 15 16 if(curl){ 17 18 curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); // On défini la destination de la requete 19 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, download); // On appelle la fonction de callback 20 curl_easy_setopt(curl, CURLOPT_WRITEDATA, &reponse); // On écrit le résultat (le contenu du download) dans le std::string reponse. 21 curl_easy_perform(curl); // On lance la requete 22 curl_easy_cleanup(curl); 23 file << reponse; // On ecrit dans le fichier 24 25 return 1; 26 }else 27 return 0; 28} 29 30std::string read(std::string url){ // Retourne la source de l'url 31 std::string source(""); 32 CURL *curl; 33 curl = curl_easy_init(); 34 35 if(curl){ 36 37 curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); // Même commentaire qu'avant, on défini la source 38 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, download); // Fonction de callback 39 curl_easy_setopt(curl, CURLOPT_WRITEDATA, &source); // On stocke le résultat 40 curl_easy_perform(curl); // on lance la requete 41 curl_easy_cleanup(curl); 42 } 43 return source; // On retourne la source 44} 45 46std::string sendPost(std::map<std::string,std::string>& data, std::string url){ // Envoi des champs $_POST à une page et retourne la réponse de la page 47 std::string post = "", source=""; 48 CURL *curl; 49 while(!data.empty()){ // On parcour la map (tableau associatif de la STL) 50 post+=data.begin()->first; // On récupère le nom de la clé 51 post+="="; 52 post+=data.begin()->second; // et sa valeur 53 post+="&"; 54 data.erase(data.begin()); // On supprime au fur et à mesure les valeurs de la map 55 } 56 57 curl = curl_easy_init(); 58 if(curl) { 59 curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); // On défini l'url destination 60 curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post.c_str()); // Les paramètres à envoyer en POST 61 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, download); // Fonction de callback 62 curl_easy_setopt(curl, CURLOPT_WRITEDATA, &source); // Stockage du résultat (réponse de la page) 63 curl_easy_perform(curl); 64 curl_easy_cleanup(curl); 65 } 66 67 return source; // On retourne le contenu 68}
Voici pour les fonctions qui se trouvent dans notre simpleweb.c, il va falloir maintenant nous attaquer à la compilation de la bibliothèque à l'aide de g++
g++ -fpic -c simpleweb.c
On obtient maintenant un simpleweb.o
g++ -shared -o libsimpleweb.so simpleweb.o
Le nom du fichier de la shared library doit être sous la forme lib*.so
Maintenant, nous allons proceder de façon un peu brutal mais efficace pour installer notre bilbiothèque sur le système,
cp libsimpleweb.so /usr/local/lib
cp libsimpleweb.so /usr/lib
Et puis pour le header
cp simpleweb.h /usr/local/include
cp simpleweb.h /usr/include
Voilà normalement cela devrait fonctionner, nous allons maintenant essayer un programme test pour voir si la bibliothèque fonctionne réellement :
Ma bibliothèque nécéssite libCurl je dois donc également fournir ce flag pour la compilation, et sinon, le flag est le nom de votre bibliothèque sans le "lib" devant et le ".so".
Si quelque chose est peu clair ou faux, n'hésitez pas à me contacter pour que je le corrige.
Retrouvez l'évolution du projet sur https://github.com/ex0ns/socket-web
ex0ns