Como você faz uma solicitação HTTP com o C ++?

Existe alguma maneira de fazer facilmente uma solicitação HTTP com C ++? Especificamente, eu quero baixar o conteúdo de uma página (uma API) e verificar o conteúdo para ver se ele contém um 1 ou um 0. Também é possível fazer o download do conteúdo em uma string?

Eu tive o mesmo problema. libcurl é realmente completo. Há um curlpp de wrapper C ++ que pode lhe interessar quando você solicita uma biblioteca C ++. neon é outra interessante biblioteca C que também suporta webdav.

O curlpp parece natural se você usa o C ++. Existem muitos exemplos fornecidos na distribuição de origem. Para obter o conteúdo de um URL, você faz algo assim (extraído dos exemplos):

// Edit : rewritten for cURLpp 0.7.3 // Note : namespace changed, was cURLpp in 0.7.2 ... #include  #include  // RAII cleanup curlpp::Cleanup myCleanup; // Send request and get a result. // Here I use a shortcut to get it in a string stream ... std::ostringstream os; os << curlpp::options::Url(std::string("http://www.wikipedia.org")); string asAskedInQuestion = os.str(); 

Veja o diretório de examples na distribuição de fonts do curlpp , há muitos casos mais complexos.

meus 2 centavos ...

Código do Windows:

 #include  #include  #include  #include  #include  #include  #include  using namespace std; #pragma comment(lib,"ws2_32.lib") int main( void ){ WSADATA wsaData; SOCKET Socket; SOCKADDR_IN SockAddr; int lineCount=0; int rowCount=0; struct hostent *host; locale local; char buffer[10000]; int i = 0 ; int nDataLength; string website_HTML; // website url string url = "www.google.com"; //HTTP GET string get_http = "GET / HTTP/1.1\r\nHost: " + url + "\r\nConnection: close\r\n\r\n"; if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0){ cout << "WSAStartup failed.\n"; system("pause"); //return 1; } Socket=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); host = gethostbyname(url.c_str()); SockAddr.sin_port=htons(80); SockAddr.sin_family=AF_INET; SockAddr.sin_addr.s_addr = *((unsigned long*)host->h_addr); if(connect(Socket,(SOCKADDR*)(&SockAddr),sizeof(SockAddr)) != 0){ cout << "Could not connect"; system("pause"); //return 1; } // send GET / HTTP send(Socket,get_http.c_str(), strlen(get_http.c_str()),0 ); // recieve html while ((nDataLength = recv(Socket,buffer,10000,0)) > 0){ int i = 0; while (buffer[i] >= 32 || buffer[i] == '\n' || buffer[i] == '\r'){ website_HTML+=buffer[i]; i += 1; } } closesocket(Socket); WSACleanup(); // Display HTML source cout< 

Aqui está uma implementação muito melhor:

 #include  #include  #include  using std::string; #pragma comment(lib,"ws2_32.lib") HINSTANCE hInst; WSADATA wsaData; void mParseUrl(char *mUrl, string &serverName, string &filepath, string &filename); SOCKET connectToServer(char *szServerName, WORD portNum); int getHeaderLength(char *content); char *readUrl2(char *szUrl, long &bytesReturnedOut, char **headerOut); int main() { const int bufLen = 1024; char *szUrl = "http://stackoverflow.com"; long fileSize; char *memBuffer, *headerBuffer; FILE *fp; memBuffer = headerBuffer = NULL; if ( WSAStartup(0x101, &wsaData) != 0) return -1; memBuffer = readUrl2(szUrl, fileSize, &headerBuffer); printf("returned from readUrl\n"); printf("data returned:\n%s", memBuffer); if (fileSize != 0) { printf("Got some data\n"); fp = fopen("downloaded.file", "wb"); fwrite(memBuffer, 1, fileSize, fp); fclose(fp); delete(memBuffer); delete(headerBuffer); } WSACleanup(); return 0; } void mParseUrl(char *mUrl, string &serverName, string &filepath, string &filename) { string::size_type n; string url = mUrl; if (url.substr(0,7) == "http://") url.erase(0,7); if (url.substr(0,8) == "https://") url.erase(0,8); n = url.find('/'); if (n != string::npos) { serverName = url.substr(0,n); filepath = url.substr(n); n = filepath.rfind('/'); filename = filepath.substr(n+1); } else { serverName = url; filepath = "/"; filename = ""; } } SOCKET connectToServer(char *szServerName, WORD portNum) { struct hostent *hp; unsigned int addr; struct sockaddr_in server; SOCKET conn; conn = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (conn == INVALID_SOCKET) return NULL; if(inet_addr(szServerName)==INADDR_NONE) { hp=gethostbyname(szServerName); } else { addr=inet_addr(szServerName); hp=gethostbyaddr((char*)&addr,sizeof(addr),AF_INET); } if(hp==NULL) { closesocket(conn); return NULL; } server.sin_addr.s_addr=*((unsigned long*)hp->h_addr); server.sin_family=AF_INET; server.sin_port=htons(portNum); if(connect(conn,(struct sockaddr*)&server,sizeof(server))) { closesocket(conn); return NULL; } return conn; } int getHeaderLength(char *content) { const char *srchStr1 = "\r\n\r\n", *srchStr2 = "\n\r\n\r"; char *findPos; int ofset = -1; findPos = strstr(content, srchStr1); if (findPos != NULL) { ofset = findPos - content; ofset += strlen(srchStr1); } else { findPos = strstr(content, srchStr2); if (findPos != NULL) { ofset = findPos - content; ofset += strlen(srchStr2); } } return ofset; } char *readUrl2(char *szUrl, long &bytesReturnedOut, char **headerOut) { const int bufSize = 512; char readBuffer[bufSize], sendBuffer[bufSize], tmpBuffer[bufSize]; char *tmpResult=NULL, *result; SOCKET conn; string server, filepath, filename; long totalBytesRead, thisReadSize, headerLen; mParseUrl(szUrl, server, filepath, filename); ///////////// step 1, connect ////////////////////// conn = connectToServer((char*)server.c_str(), 80); ///////////// step 2, send GET request ///////////// sprintf(tmpBuffer, "GET %s HTTP/1.0", filepath.c_str()); strcpy(sendBuffer, tmpBuffer); strcat(sendBuffer, "\r\n"); sprintf(tmpBuffer, "Host: %s", server.c_str()); strcat(sendBuffer, tmpBuffer); strcat(sendBuffer, "\r\n"); strcat(sendBuffer, "\r\n"); send(conn, sendBuffer, strlen(sendBuffer), 0); // SetWindowText(edit3Hwnd, sendBuffer); printf("Buffer being sent:\n%s", sendBuffer); ///////////// step 3 - get received bytes //////////////// // Receive until the peer closes the connection totalBytesRead = 0; while(1) { memset(readBuffer, 0, bufSize); thisReadSize = recv (conn, readBuffer, bufSize, 0); if ( thisReadSize <= 0 ) break; tmpResult = (char*)realloc(tmpResult, thisReadSize+totalBytesRead); memcpy(tmpResult+totalBytesRead, readBuffer, thisReadSize); totalBytesRead += thisReadSize; } headerLen = getHeaderLength(tmpResult); long contenLen = totalBytesRead-headerLen; result = new char[contenLen+1]; memcpy(result, tmpResult+headerLen, contenLen); result[contenLen] = 0x0; char *myTmp; myTmp = new char[headerLen+1]; strncpy(myTmp, tmpResult, headerLen); myTmp[headerLen] = NULL; delete(tmpResult); *headerOut = myTmp; bytesReturnedOut = contenLen; closesocket(conn); return(result); } 

No Linux, eu tentei o cpp-netlib, o libcurl, o curlpp, o urdl, o boost :: asio e considerei o Qt (mas o desativei com base na licença). Todos eles estavam incompletos para esse uso, tinham interfaces desleixadas, tinham documentação deficiente, eram não-mantidos ou não suportavam https.

Então, por sugestão de https://stackoverflow.com/a/1012577/278976 , eu tentei POCO. Uau, eu gostaria de ter visto isso anos atrás. Aqui está um exemplo de como fazer uma solicitação HTTP GET:

http://xjia.heroku.com/2011/09/10/learning-poco-get-with-http/

O POCO é gratuito e de código aberto (licença de impulso). E não, eu não tenho nenhuma afiliação com a empresa; Eu realmente gosto de suas interfaces. Bom trabalho pessoal (e galões).

http://pocoproject.org/download/index.html

Espero que isso ajude alguém … levei três dias para testar todas essas bibliotecas.

Há um wrapper de onda mais recente e menos maduro sendo desenvolvido chamado Solicitações de C ++ . Aqui está uma simples solicitação GET:

 #include  #include  int main(int argc, char** argv) { auto response = cpr::Get(cpr::Url{"http://httpbin.org/get"}); std::cout << response.text << std::endl; } 

Ele suporta uma ampla variedade de verbos HTTP e opções de curl. Há mais documentação de uso aqui .

Isenção de responsabilidade: Eu sou o mantenedor desta biblioteca .

Como você quer uma solução C ++, você pode usar o Qt . Tem uma class QHttp que você pode usar.

Você pode verificar os documentos :

 http->setHost("qt.nokia.com"); http->get(QUrl::toPercentEncoding("/index.html")); 

O Qt também tem muito mais que você poderia usar em um aplicativo C ++ comum.

O libCURL é uma ótima opção para você. Dependendo do que você precisa fazer, o tutorial deve lhe dizer o que você quer, especificamente para o manuseio fácil. Mas, basicamente, você poderia fazer isso apenas para ver a origem de uma página:

 CURL* c; c = curl_easy_init(); curl_easy_setopt( c, CURL_URL, "www.google.com" ); curl_easy_perform( c ); curl_easy_cleanup( c ); 

Eu acredito que isso fará com que o resultado seja impresso no stdout. Se você quiser lidar com isso – o que, eu presumo que você faça – você precisa definir o CURL_WRITEFUNCTION. Tudo isso é coberto no tutorial de curls vinculado acima.

Aqui está meu wrapper mínimo em torno de cURL para poder buscar apenas uma página da Web como uma string. Isso é útil, por exemplo, para testes unitários. É basicamente um wrapper RAII em torno do código C.

Instale “libcurl” na sua máquina yum install libcurl libcurl-devel ou equivalente.

Exemplo de uso:

 CURLplusplus client; string x = client.Get("http://google.com"); string y = client.Get("http://yahoo.com"); 

Implementação de class:

 #include  class CURLplusplus { private: CURL* curl; stringstream ss; long http_code; public: CURLplusplus() : curl(curl_easy_init()) , http_code(0) { } ~CURLplusplus() { if (curl) curl_easy_cleanup(curl); } std::string Get(const std::string& url) { CURLcode res; curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data); curl_easy_setopt(curl, CURLOPT_WRITEDATA, this); ss.str(""); http_code = 0; res = curl_easy_perform(curl); if (res != CURLE_OK) { throw std::runtime_error(curl_easy_strerror(res)); } curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); return ss.str(); } long GetHttpCode() { return http_code; } private: static size_t write_data(void *buffer, size_t size, size_t nmemb, void *userp) { return static_cast(userp)->Write(buffer,size,nmemb); } size_t Write(void *buffer, size_t size, size_t nmemb) { ss.write((const char*)buffer,size*nmemb); return size*nmemb; } }; 

Com esta resposta, refiro-me à resposta do Software_Developer . Recriando o código, descobri que algumas partes estão obsoletas ( gethostbyname() ) ou não fornecem tratamento de erros (criação de sockets, envio de algo) para uma operação.

O código do Windows a seguir é testado com o Visual Studio 2013 e o Windows 8.1 de 64 bits, além do Windows 7 de 64 bits. Ele segmentará uma conexão TCP IPv4 com o servidor da Web de http://www.google.com.

 #include  #include  #include  #include  #pragma comment(lib,"ws2_32.lib") using namespace std; int main (){ // Initialize Dependencies to the Windows Socket. WSADATA wsaData; if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0) { cout << "WSAStartup failed.\n"; system("pause"); return -1; } // We first prepare some "hints" for the "getaddrinfo" function // to tell it, that we are looking for a IPv4 TCP Connection. struct addrinfo hints; ZeroMemory(&hints, sizeof(hints)); hints.ai_family = AF_INET; // We are targeting IPv4 hints.ai_protocol = IPPROTO_TCP; // We are targeting TCP hints.ai_socktype = SOCK_STREAM; // We are targeting TCP so its SOCK_STREAM // Aquiring of the IPv4 address of a host using the newer // "getaddrinfo" function which outdated "gethostbyname". // It will search for IPv4 addresses using the TCP-Protocol. struct addrinfo* targetAdressInfo = NULL; DWORD getAddrRes = getaddrinfo("www.google.com", NULL, &hints, &targetAdressInfo); if (getAddrRes != 0 || targetAdressInfo == NULL) { cout << "Could not resolve the Host Name" << endl; system("pause"); WSACleanup(); return -1; } // Create the Socket Address Informations, using IPv4 // We dont have to take care of sin_zero, it is only used to extend the length of SOCKADDR_IN to the size of SOCKADDR SOCKADDR_IN sockAddr; sockAddr.sin_addr = ((struct sockaddr_in*) targetAdressInfo->ai_addr)->sin_addr; // The IPv4 Address from the Address Resolution Result sockAddr.sin_family = AF_INET; // IPv4 sockAddr.sin_port = htons(80); // HTTP Port: 80 // We have to free the Address-Information from getaddrinfo again freeaddrinfo(targetAdressInfo); // Creation of a socket for the communication with the Web Server, // using IPv4 and the TCP-Protocol SOCKET webSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (webSocket == INVALID_SOCKET) { cout << "Creation of the Socket Failed" << endl; system("pause"); WSACleanup(); return -1; } // Establishing a connection to the web Socket cout << "Connecting...\n"; if(connect(webSocket, (SOCKADDR*)&sockAddr, sizeof(sockAddr)) != 0) { cout << "Could not connect"; system("pause"); closesocket(webSocket); WSACleanup(); return -1; } cout << "Connected.\n"; // Sending a HTTP-GET-Request to the Web Server const char* httpRequest = "GET / HTTP/1.1\r\nHost: www.google.com\r\nConnection: close\r\n\r\n"; int sentBytes = send(webSocket, httpRequest, strlen(httpRequest),0); if (sentBytes < strlen(httpRequest) || sentBytes == SOCKET_ERROR) { cout << "Could not send the request to the Server" << endl; system("pause"); closesocket(webSocket); WSACleanup(); return -1; } // Receiving and Displaying an answer from the Web Server char buffer[10000]; ZeroMemory(buffer, sizeof(buffer)); int dataLen; while ((dataLen = recv(webSocket, buffer, sizeof(buffer), 0) > 0)) { int i = 0; while (buffer[i] >= 32 || buffer[i] == '\n' || buffer[i] == '\r') { cout << buffer[i]; i += 1; } } // Cleaning up Windows Socket Dependencies closesocket(webSocket); WSACleanup(); system("pause"); return 0; } 

Referências:

Deprecação de gethostbyname

Valor de retorno do soquete ()

Valor de retorno de send ()

Você pode querer verificar o C ++ REST SDK (codinome “Casablanca”). http://msdn.microsoft.com/pt-br/library/jj950081.aspx

Com o C ++ REST SDK, você pode se conectar com mais facilidade aos servidores HTTP a partir do seu aplicativo C ++.

Exemplo de uso:

 #include  #include  using namespace web::http; // Common HTTP functionality using namespace web::http::client; // HTTP client features int main(int argc, char** argv) { http_client client("http://httpbin.org/"); http_response response; // ordinary `get` request response = client.request(methods::GET, "/get").get(); std::cout << response.extract_string().get() << "\n"; // working with json response = client.request(methods::GET, "/get").get(); std::cout << "url: " << response.extract_json().get()[U("url")] << "\n"; } 

O C ++ REST SDK é um projeto da Microsoft para comunicação cliente-servidor baseado em nuvem em código nativo usando um design de API C ++ asynchronous moderno.

C ++ não fornece nenhuma maneira de fazer isso diretamente. Dependeria inteiramente de quais plataformas e bibliotecas você possui.

Na pior das hipóteses, você pode usar a biblioteca boost :: asio para estabelecer uma conexão TCP, enviar os headers HTTP (RFC 2616) e analisar as respostas diretamente. Olhando para as necessidades da sua aplicação, isso é bastante simples de fazer.

C e C ++ não possuem uma biblioteca padrão para HTTP ou mesmo para conexões de soquete. Ao longo dos anos, algumas bibliotecas portáteis foram desenvolvidas. O mais amplamente usado, como outros disseram, é libcurl .

Aqui está uma lista de alternativas ao libcurl (vindas do site do libcurl).

Além disso, para o Linux, este é um cliente HTTP simples. Você poderia implementar seu próprio cliente HTTP GET simples, mas isso não funcionará se houver autenticação ou redirecionamentos envolvidos ou se você precisar trabalhar atrás de um proxy. Para esses casos, você precisa de uma biblioteca completa como libcurl.

Para o código fonte com libcurl, este é o mais próximo do que você quer (Libcurl tem muitos exemplos ). Veja a function principal. O conteúdo html será copiado para o buffer, após uma conexão bem-sucedida. Basta replace parseHtml por sua própria function.

Você pode usar a biblioteca embeddedRest . É uma biblioteca apenas de header leve. Portanto, é fácil incluí-lo em seu projeto e não requer compilation, pois não há arquivos .cpp nele.

Solicite um exemplo de readme.md do repo:

 #include "UrlRequest.hpp" //... UrlRequest request; request.host("api.vk.com"); const auto countryId=1; const auto count=1000; request.uri("/method/database.getCities",{ {"lang","ru"}, {"country_id",countryId}, {"count",count}, {"need_all","1"}, }); request.addHeader("Content-Type: application/json"); auto response=std::move(request.perform()); if(response.statusCode()==200){ cout<<"status code = "<
		      	

Aqui está um código C ++ 11 (relativamente) simples que usa libCURL para baixar o conteúdo de uma URL em um std::vector :

http_download.hh

 # pragma once #include  #include  std::vector download(std::string url, long* responseCode = nullptr); 

http_download.cc

 #include "http_download.hh" #include  #include  #include  using namespace std; size_t callback(void* contents, size_t size, size_t nmemb, void* user) { auto chunk = reinterpret_cast(contents); auto buffer = reinterpret_cast*>(user); size_t priorSize = buffer->size(); size_t sizeIncrease = size * nmemb; buffer->resize(priorSize + sizeIncrease); std::copy(chunk, chunk + sizeIncrease, buffer->data() + priorSize); return sizeIncrease; } vector download(string url, long* responseCode) { vector data; curl_global_init(CURL_GLOBAL_ALL); CURL* handle = curl_easy_init(); curl_easy_setopt(handle, CURLOPT_URL, url.c_str()); curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, callback); curl_easy_setopt(handle, CURLOPT_WRITEDATA, &data); curl_easy_setopt(handle, CURLOPT_USERAGENT, "libcurl-agent/1.0"); CURLcode result = curl_easy_perform(handle); if (responseCode != nullptr) curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, responseCode); curl_easy_cleanup(handle); curl_global_cleanup(); if (result != CURLE_OK) { stringstream err; err << "Error downloading from URL \"" << url << "\": " << curl_easy_strerror(result); throw runtime_error(err.str()); } return move(data); } 

Geralmente, eu recomendaria algo de plataforma cruzada como cURL, POCO ou Qt. No entanto, aqui está um exemplo do Windows !:

 #include  #include  #include  // _bstr_t HRESULT hr; CComPtr request; hr = request.CoCreateInstance(CLSID_XMLHTTP60); hr = request->open( _bstr_t("GET"), _bstr_t("http://sofpt.miximages.com/c%2B%2B/logo11w.png"), _variant_t(VARIANT_FALSE), _variant_t(), _variant_t()); hr = request->send(_variant_t()); // get status - 200 if succuss long status; hr = request->get_status(&status); // load image data (if url points to an image) VARIANT responseVariant; hr = request->get_responseStream(&responseVariant); IStream* stream = (IStream*)responseVariant.punkVal; CImage *image = new CImage(); image->Load(stream); stream->Release(); 

O protocolo HTTP é muito simples, então é muito simples escrever um cliente HTTP. Aqui está um

https://github.com/pedro-vicente/lib_netsockets

Ele usa HTTP GET para recuperar um arquivo de um servidor da Web, tanto o servidor quanto o arquivo são parâmetros de linha de comando. O arquivo remoto é salvo em uma cópia local.

Disclaimer: Eu sou o autor

EDIT: URL editada

Observe que isso não requer libcurl, Windows.h ou WinSock! Nenhuma compilation de bibliotecas, nenhuma configuração de projeto, etc. Eu tenho esse código trabalhando no Visual Studio 2017 c ++ no Windows 10:

 #pragma comment(lib, "urlmon.lib") #include  #include  using namespace std; ... IStream* stream; //Also works with https URL's - unsure about the extent of SSL support though. HRESULT result = URLOpenBlockingStream(0, "http://google.com", &stream, 0, 0); if (result != 0) { return 1; } char buffer[100]; unsigned long bytesRead; stringstream ss; stream->Read(buffer, 100, &bytesRead); while (bytesRead > 0U) { ss.write(buffer, (long long)bytesRead); stream->Read(buffer, 100, &bytesRead); } stream.Release(); string resultString = ss.str(); 

Eu acabei de descobrir como fazer isso, como eu queria um simples script de access à API, bibliotecas como libcurl estavam me causando todos os tipos de problemas (mesmo quando eu seguia as instruções …), e WinSock é muito baixo nível e complicado .

Eu não tenho certeza sobre todo o código de leitura IStream (particularmente a condição while – sinta-se livre para corrigir / melhorar), mas ei, funciona , sem complicações! (Faz sentido para mim que, desde que eu usei uma chamada de bloqueio (síncrona) , isso é bom, que bytesRead sempre seria> 0U até que o stream ( ISequentialStream ?) Termine de ser lido, mas quem sabe.)

Consulte também: Marcadores de URL e Referência de Protocolo Pluggable Assíncrona

Embora um pouco atrasado. Você pode preferir https://github.com/Taymindis/backcurl .

Ele permite que você faça chamadas http no desenvolvimento c ++ móvel. Adequado para desenvolvimento de jogos para dispositivos móveis

 bcl::init(); // init when using bcl::execute([&](bcl::Request *req) { bcl::setOpts(req, CURLOPT_URL , "http://www.google.com", CURLOPT_FOLLOWLOCATION, 1L, CURLOPT_WRITEFUNCTION, &bcl::writeContentCallback, CURLOPT_WRITEDATA, req->dataPtr, CURLOPT_USERAGENT, "libcurl-agent/1.0", CURLOPT_RANGE, "0-200000" ); }, [&](bcl::Response * resp) { std::string ret = std::string(resp->getBody()->c_str()); printf("Sync === %s\n", ret.c_str()); }); bcl::cleanUp(); // clean up when no more using 

Você pode usar o ACE para fazer isso:

 #include "ace/SOCK_Connector.h" int main(int argc, ACE_TCHAR* argv[]) { //HTTP Request Header char* szRequest = "GET /video/nice.mp4 HTTP/1.1\r\nHost: example.com\r\n\r\n"; int ilen = strlen(szRequest); //our buffer char output[16*1024]; ACE_INET_Addr server (80, "example.com"); ACE_SOCK_Stream peer; ACE_SOCK_Connector connector; int ires = connector.connect(peer, server); int sum = 0; peer.send(szRequest, ilen); while (true) { ACE_Time_Value timeout = ACE_Time_Value(15); int rc = peer.recv_n(output, 16*1024, &timeout); if (rc == -1) { break; } sum += rc; } peer.close(); printf("Bytes transffered: %d",sum); return 0; }