Per altre informazioni scrivi a fabriziocaldarelli@negusweb.it
Socket Http Client in C++ su Linux (GET)
Da Programmazione Software.
Descrizione
Capita spesso di dover accedere a pagine web attraverso C++, molto spesso per accedere al contenuto di risorse XML tramite protocollo http, quindi usando il metodo GET.
Qui di seguito il codice sorgente di un client Http (solo metodo GET) incapsulato in un'apposita classe, in modo da avere codice pulito ed organizzato.
Il client si inizializza passando HostName e Pagina da visualizzare (in realtà si può fare un metodo che splitta una url completa, magari questo nella versione 2.0), dando per scontato che la porta di connessione sia l'80.
Al momento è possibile solo usare il metodo GET, probabilmente seguirà una nuova versione con il metodo POST, il passaggio di una url completa al costruttore e la possibilità di definire la porta.
Insieme al client, un piccolo programma per utilizzarlo che scrive in un path specifico (in questo caso /home/negus) i files relativi all'header ed al body della risposta.
Alla fine del codice lo zip del programma.
Buona lettura.
inc/HttpClient/CHttpClient.h
/** CHttpClient.h** Created on: 11-mar-2009* Author: Fabrizio Caldarelli* Mail: fabriziocaldarelli@negusweb.it*/#ifndef CHTTPCLIENT_H_#include <stdio.h>#include <sys socket.h="">#include <arpa inet.h="">#include <stdlib.h>#include <netdb.h>#include <string.h>#include <iostream>#include <string>using namespace std;
class CHttpClient{public: // Publics Methods
int DoHttpGetRequest(string& oHttpHeader, string& oHttpBody);
public: // Constructors
CHttpClient(const string& iHostName, const string& iPage);
private: // Privates Methods
int _CreateTcpSocket();
int _GetIp(const string& iHostName,string& oIp);
int _BuildGetQuery(const string& iHostName, const string& iPage, string& oQueryGet);
private: // Privates Members
string hostName;string page;};
#define CHTTPCLIENT_H_#endif /* CHTTPCLIENT_H_ */
inc/HttpClient/CHttpClient.cpp
/** CHttpClient.cpp** Created on: 11-mar-2009* Author: Fabrizio Caldarelli* Mail: fabriziocaldarelli@negusweb.it*/#include "HttpClient/CHttpClient.h"int CHttpClient::_CreateTcpSocket()
{int sock;
if((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0){
perror("Can't create TCP socket");
exit(1);
}return sock;
}int CHttpClient::_GetIp(const string& iHostName,string &oIp)
{struct hostent *hent; // distruggere *hent
int iplen = 15; //XXX.XXX.XXX.XXX
char *ip = (char *)malloc(iplen+1); // distruggere *ip
memset(ip, 0, iplen+1);
if((hent = gethostbyname(iHostName.c_str())) == NULL)
{herror("Can't get IP");
exit(1);
}if(inet_ntop(AF_INET, (void *)hent->h_addr_list[0], ip, iplen) == NULL)
{perror("Can't resolve host");
exit(1);
}// Assign value output parameter oIpoIp.append(ip);
free(ip);
// Function return codereturn 0;
}int CHttpClient::_BuildGetQuery(const string& iHostName,const string& iPage,string& oQueryGet)
{char *query; // distruggere *query
char *getPage = new char[iPage.length()+1]; // distruggere *getPage
strcpy(getPage,iPage.c_str());
string UserAgent("HTMLGET 1.0");
char *tpl = "GET /%s HTTP/1.0\r\nHost: %s\r\nUser-Agent: %s\r\n\r\n"; // distruggere *tpl
if(getPage[0] == '/'){
getPage = getPage + 1;
fprintf(stderr,"Removing leading \"/\", converting %s to %s\n", iPage.c_str(), getPage);
}// -5 is to consider the %s %s %s in tpl and the ending \0query = (char *)malloc(iHostName.length()+strlen(getPage)+UserAgent.length()+strlen(tpl)-5);
sprintf(query, tpl, getPage, iHostName.c_str(), UserAgent.c_str());
// Assign Query Get Output valueoQueryGet = query;
// Free memoryfree(query);
delete [] getPage;
// Function return codereturn 0;
}CHttpClient::CHttpClient(const string& iHostName, const string& iPage)
{this->hostName.append(iHostName);
this->page.append(iPage);
}int CHttpClient::DoHttpGetRequest(string& oHttpHeader,string& oHttpBody)
{char buf[BUFSIZ+1];
int tmpres;
int sock;
string ip;string queryGet;struct sockaddr_in remote;
sock = this->_CreateTcpSocket();
this->_GetIp(this->hostName, ip);
fprintf(stderr, "IP is %s\n", ip.c_str());
//remote = (struct sockaddr_in *)malloc(sizeof(struct sockaddr_in *));remote.sin_family = AF_INET;
tmpres = inet_pton(AF_INET, ip.c_str(), (void *)(&(remote.sin_addr.s_addr)));
if( tmpres < 0)
{perror("Can't set remote->sin_addr.s_addr");
exit(1);
}else if(tmpres == 0)
{fprintf(stderr, "%s is not a valid IP address\n", ip);
exit(1);
}remote.sin_port = htons(80);
if(connect(sock, (struct sockaddr *)&remote, sizeof(struct sockaddr)) < 0){
perror("Could not connect");
exit(1);
}this->_BuildGetQuery(this->hostName,this->page,queryGet);
fprintf(stderr, "Query is:\n<<start>>\n%s<<end>>\n", queryGet.c_str());
//Send the query to the serverint sent = 0;
while(sent < queryGet.length())
{tmpres = send(sock, queryGet.c_str()+sent, queryGet.length()-sent, 0);
if(tmpres == -1){
perror("Can't send query");
exit(1);
}sent += tmpres;
}//now it is time to receive the pagememset(buf, 0, sizeof(buf));
int htmlstart = 0;
char * htmlcontent;
char* chHttpResponse;
string httpResponse = "";
while((tmpres = recv(sock, buf, BUFSIZ, 0)) > 0)
{chHttpResponse = new char[tmpres+1];
strncpy(chHttpResponse,buf,tmpres);
httpResponse.append(chHttpResponse);
delete [] chHttpResponse;
memset(buf, 0, tmpres);
}if(tmpres < 0)
{perror("Error receiving data");
}// Split httpResponse in httpHeader and httpBodyint separator = httpResponse.find("\r\n\r\n");
// If separator not found, return npos. So, only header and not bodyoHttpHeader = "";
oHttpBody = "";
if (separator == string::npos)
{oHttpHeader.append(httpResponse);
}else{oHttpHeader.append(httpResponse.substr(0,separator));
separator += 4;
oHttpBody.append(httpResponse.substr(separator,httpResponse.length()-separator));
}close(sock);
return 0;
}

