Comment faire une requête HTTP avec C ++?

258

Existe-t-il un moyen de faire facilement une requête HTTP avec C ++? Plus précisément, je souhaite télécharger le contenu d'une page (une API) et vérifier le contenu pour voir s'il contient un 1 ou un 0. Est-il également possible de télécharger le contenu dans une chaîne?

Sam152
la source
1
Non, il n'y a actuellement aucun support intégré via la langue ou la bibliothèque standard pour la mise en réseau. Cependant, il existe un TS Networking N4370 . J'ai également VTC cette question car elle attire des recommandations de la bibliothèque.
Et BoostBeast?
twocrush

Réponses:

250

J'ai eu le même problème. libcurl est vraiment complet. Il existe un curlpp wrapper C ++ qui pourrait vous intéresser lorsque vous demandez une bibliothèque C ++. neon est une autre bibliothèque C intéressante qui prend également en charge WebDAV .

curlpp semble naturel si vous utilisez C ++. Il existe de nombreux exemples fournis dans la distribution source. Pour obtenir le contenu d'une URL, vous faites quelque chose comme ça (extrait d'exemples):

// Edit : rewritten for cURLpp 0.7.3
// Note : namespace changed, was cURLpp in 0.7.2 ...

#include <curlpp/cURLpp.hpp>
#include <curlpp/Options.hpp>

// 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();

Voir le examplesrépertoire dans la distribution source de curlpp , il y a beaucoup de cas plus complexes, ainsi qu'un cas simple, complet et minimal utilisant curlpp.

mes 2 cents ...

neuro
la source
1
la dernière version semble être cassée sous mac .. quelque chose est gâché avec config.h lorsqu'il est lié en tant que bibliothèque.
eugene
1
Eh bien, je n'ai pas pu compiler ce qui précède. Cependant, le remplacement os << myRequest.perform();par a myRequest.setOpt( new curlpp::options::WriteStream( &os ) ); myRequest.perform();donné des résultats. Assurez-vous de ne pas utiliser http://example.com, cela retournera une page vide. Mieux utiliser par exemple http://www.wikipedia.org.
Zane
4
Comment créez-vous curlpp dans MSVS? Je ne peux pas le faire fonctionner :(
mr5
2
Je ne suis pas d'accord avec la dernière modification de @ ryan-sam. C'était clairement l'intention de l'auteur d'écrire "webdav" et non le développement web car la bibliothèque donnée est faite explicitement pour les "opérations HTTP et WebDAV".
Bostrot
2
@bostrot: Oui c'est ce que je voulais dire. J'ai rétabli et ajouté un lien, je pense que les gens pensaient que j'avais écrit webdev. Quel dommage :)
neuro
115

Code Windows:

#include <string.h>
#include <winsock2.h>
#include <windows.h>
#include <iostream>
#include <vector>
#include <locale>
#include <sstream>
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<<website_HTML;

    // pause
    cout<<"\n\nPress ANY key to close.\n\n";
    cin.ignore(); cin.get(); 


 return 0;
}

Voici une bien meilleure implémentation:

#include <windows.h>
#include <string>
#include <stdio.h>

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);
}
Concepteur de logiciels
la source
J'ai essayé ce code sur un Windows Vista compilant avec Dev-C ++ Version 4.9.9.2. Je me suis donné un tas d'erreurs lors de la liaison: [Erreur de l'éditeur de liens] référence non définie à `WSAStartup @ 8 '
Expanding-Dev
4
@ Expanding-Dev Only MSVC (Visual Studio) comprend le "commentaire pragma". Si vous utilisez autre chose, vous devez lier manuellement "ws2_32.lib" (comme toute autre bibliothèque).
Navin
24
@JuanLuisSoldi Je suppose que vous devez vraiment être un développeur Windows pour apprécier la "beauté" de ce code ...
static_rtti
Que doit-on recevoir (en utilisant recv) ici? Je reçois beaucoup de charabia en sortie. Aussi, pourquoi avez-vous mis ce que vous avez fait dans le tampon d'envoi (par exemple GET / HTTP/1.1.1/... etc)? Comment savoir comment formater ce que j'envoie?
LazerSharks
43

Mise à jour 2020: j'ai une nouvelle réponse qui remplace celle-ci, maintenant âgée de 8 ans, une: https://stackoverflow.com/a/61177330/278976

Sous Linux, j'ai essayé cpp-netlib, libcurl, curlpp, urdl, boost :: asio et j'ai considéré Qt (mais je l'ai refusé en fonction de la licence). Tous ces éléments étaient incomplets pour cette utilisation, avaient des interfaces bâclées, avaient une mauvaise documentation, n'étaient pas entretenus ou ne prenaient pas en charge https.

Ensuite, à la suggestion de https://stackoverflow.com/a/1012577/278976 , j'ai essayé POCO. Wow, j'aurais aimé voir ça il y a des années. Voici un exemple de création d'une demande HTTP GET avec POCO:

https://stackoverflow.com/a/26026828/2817595

POCO est gratuit, open source (licence boost). Et non, je n'ai aucune affiliation avec l'entreprise; J'aime vraiment leurs interfaces. Grands gars de travail (et filles).

https://pocoproject.org/download.html

J'espère que cela aide quelqu'un ... il m'a fallu trois jours pour essayer toutes ces bibliothèques.

Homer6
la source
1
Voici un exemple supplémentaire: github.com/pocoproject/poco/blob/develop/Net/samples/httpget/…
Homer6
2
Je viens de télécharger Poco sur votre suggestion. Je préférerais quelque chose de léger qui s'appuie sur STL et boost plutôt que de réécrire une grande partie. De plus, je ne suis pas un fan de CppUnit et en particulier des tests de haine exécutés avec la build, et je ne m'attends pas à devoir tester leur bibliothèque pendant que je la construis.
CashCow du
C'est un peu gros. Cependant, vous pouvez désactiver la construction des tests et des échantillons (ou des bibliothèques partagées) avec configure (c'est-à-dire --no-tests ou --no-samples ou --no-sharedlibs). Voir github.com/pocoproject/poco/blob/develop/configure
Homer6
Merci pour ça. Je le veux quand même car je me soucie de faire les tâches que je dois faire. Et je note qu'ils ont également une analyse JSON, ce qui est bien car j'aurai besoin de le faire après avoir envoyé la requête HTTP pour laquelle j'ai obtenu la bibliothèque.
CashCow
c'était il y a un certain temps, mais c'est juste pour vous faire savoir qu'aucun de vos liens ne fonctionne maintenant, même le dépôt github ne semble pas avoir été supprimé ...
Hack06
33

Un wrapper de boucle plus récent et moins mature est en cours de développement, appelé Requêtes C ++ . Voici une simple demande GET:

#include <iostream>
#include <cpr.h>

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

Il prend en charge une grande variété d'options de verbes et de boucles HTTP. Il y a plus de documentation d'utilisation ici .

Avertissement: je suis le mainteneur de cette bibliothèque .

huu
la source
10
Hier, j'étais à votre discours éclair sur CppCon 2015. Bravo - à la fois la conférence et la bibliothèque. J'aime particulièrement la philosophie de conception "Curl for people".
U007D
Bonjour, je viens de tomber sur ce post ici, à la recherche de requêtes HTTP C ++ plus simples que la manière simple. Cependant, je n'ai pas vraiment d'expérience avec les bibliothèques, et je ne sais pas vraiment comment l'inclure dans mon projet Visual Studio C ++. Y a-t-il une explication quelque part? J'ai l'impression que ce n'est pas spécifique à la bibliothèque, mais plutôt que je ne sais pas vraiment quoi faire de ce que j'ai devant moi maintenant en général.
Sossenbinder
2
@Sossenbinder, si vous pouvez vous familiariser avec CMake, vous pouvez générer des fichiers de construction Visual Studio pour ce projet en utilisant cela. Le fichier de configuration de l'appletor contient un exemple approximatif de la façon de procéder.
huu
2
Ça a l'air bien, mais la construction est un enfer, donc votre bibliothèque est inutile, je ne peux pas compter sur le gestionnaire de paquets (besoin d'un moyen fiable pour ajouter des dépôts en externe) et j'ai besoin d'une bibliothèque fonctionnelle dès que possible ...
dev1223
C'est comme ça que tu le fais. lorsque vous comparez cela aux 200 lignes de la deuxième réponse la plus appréciée .......
v.oddou
17

Voici mon enveloppe minimale autour de cURL pour pouvoir simplement récupérer une page Web sous forme de chaîne. Ceci est utile, par exemple, pour les tests unitaires. Il s'agit essentiellement d'un wrapper RAII autour du code C.

Installez "libcurl" sur votre machine yum install libcurl libcurl-develou équivalent.

Exemple d'utilisation:

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

Implémentation de classe:

#include <curl/curl.h>


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<CURLplusplus*>(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;
    }
};
Mark Lakata
la source
16

libCURL est une assez bonne option pour vous. Selon ce que vous devez faire, le didacticiel devrait vous dire ce que vous voulez, en particulier pour la poignée facile. Mais, en gros, vous pouvez le faire juste pour voir la source d'une page:

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

Je crois que cela entraînera l'impression du résultat sur stdout. Si vous voulez le gérer à la place - ce qui, je suppose, vous le faites - vous devez définir la CURL_WRITEFUNCTION. Tout cela est couvert dans le tutoriel curl lié ci-dessus.

Mémoire libre
la source
16

Comme vous voulez une solution C ++, vous pouvez utiliser Qt . Il a une classe QHttp que vous pouvez utiliser.

Vous pouvez consulter les documents :

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

Qt a également beaucoup plus que vous pouvez utiliser dans une application C ++ commune.

Marcelo Santos
la source
4
Je pense que QHttp a été remplacé par QNetworkAccessManager et les classes associées dans Qt 4.6 et versions ultérieures.
juzzlin
3
QNetworkAccessManagera été documenté depuis Qt 4.4; et dans Qt 4.8 dit: QHttp - This class is obsolete. It is provided to keep old source code working. We strongly advise against using it in new code.Donc je suppose qu'il est toujours disponible, si vous ignorez les avertissements obsolètes.
Jesse Chisholm
13

Vous pouvez vérifier le SDK REST C ++ (nom de code "Casablanca"). http://msdn.microsoft.com/en-us/library/jj950081.aspx

Avec le SDK C ++ REST, vous pouvez plus facilement vous connecter aux serveurs HTTP à partir de votre application C ++.

Exemple d'utilisation:

#include <iostream>
#include <cpprest/http_client.h>

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";
}

Le SDK C ++ REST est un projet Microsoft pour la communication client-serveur basée sur le cloud en code natif à l'aide d'une conception d'API C ++ asynchrone moderne.

oxyde de cuivre
la source
10

Avec cette réponse, je me réfère à la réponse de Software_Developer . En reconstruisant le code, j'ai trouvé que certaines parties sont obsolètes ( gethostbyname()) ou ne fournissent pas de gestion des erreurs (création de sockets, envoi de quelque chose) pour une opération.

Le code Windows suivant est testé avec Visual Studio 2013 et Windows 8.1 64 bits ainsi que Windows 7 64 bits. Il ciblera une connexion TCP IPv4 avec le serveur Web de www.google.com.

#include <winsock2.h>
#include <WS2tcpip.h>
#include <windows.h>
#include <iostream>
#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;
}

Références:

Obsolescence de gethostbyname

Valeur de retour de socket ()

Valeur de retour de send ()

Vinz
la source
7

C ++ ne fournit aucun moyen de le faire directement. Cela dépendrait entièrement des plates-formes et bibliothèques dont vous disposez.

Dans le pire des cas, vous pouvez utiliser la bibliothèque boost :: asio pour établir une connexion TCP, envoyer les en-têtes HTTP (RFC 2616) et analyser directement les réponses. En regardant les besoins de votre application, c'est assez simple à faire.

sybreon
la source
1
C'est le cas - maintenant, au moins. :) stackoverflow.com/a/51959694/1599699
Andrew
@Andrew: Si votre adresse "Il le fait" répond au sybreon, "C ++ ne fournit aucun moyen de le faire directement" , alors la réponse liée n'est pas valide car elle montre un moyen de le faire en utilisant les spécificités du système.
Sebastian Mach
@SebastianMach Je veux dire, c'est le cas. Importez simplement une bibliothèque fournie par le système et appelez une fonction, et elle fera le travail pour vous. Comparez cela avec toutes les autres options c ++ et c'est soit vraiment difficile, soit en utilisant du code tiers. Je considère cela comme assez direct.
Andrew
1
@Andrew: Il est "fourni par le système" uniquement sous Windows. Sur d'autres systèmes, c'est différent. Et cela n'a rien à voir avec "C ++ ne fournit aucun moyen de le faire directement", ce qui signifie vraiment "La norme C ++ n'en a pas".
Sebastian Mach
@SebastianMach Ouais mais c'est subjectif aussi parce que c ++ fonctionne également sur les téléphones, les microcontrôleurs, les tablettes, etc. OP n'a pas dit "standard c ++", il a juste dit c ++. Ces réponses fournissent des solutions Linux et Windows, car c'est généralement ce que vous utiliseriez pour quelque chose comme ça. Alors bien sûr, ce n'est pas une solution Linux, mais oui, elle est fournie directement par un OS majeur.
Andrew
6

Voici du code qui fonctionnera sans avoir besoin d'utiliser une bibliothèque tierce: Définissez d'abord votre passerelle, votre utilisateur, votre mot de passe et tout autre paramètre que vous devez envoyer à ce serveur spécifique.

#define USERNAME "user"
#define PASSWORD "your password"
#define GATEWAY "your gateway"

Voici le code lui-même:

HINTERNET hOpenHandle, hResourceHandle, hConnectHandle;
const TCHAR* szHeaders = _T("Content-Type:application/json; charset=utf-8\r\n");


hOpenHandle = InternetOpen(_T("HTTPS"), INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
if (hOpenHandle == NULL)
{
    return false;
}


hConnectHandle = InternetConnect(hOpenHandle,
    GATEWAY,
    INTERNET_DEFAULT_HTTPS_PORT,
    NULL, NULL, INTERNET_SERVICE_HTTP,
    0, 1);

if (hConnectHandle == NULL)
{
    InternetCloseHandle(hOpenHandle);
    return false;
}


hResourceHandle = HttpOpenRequest(hConnectHandle,
    _T("POST"),
    GATEWAY,
    NULL, NULL, NULL, INTERNET_FLAG_SECURE | INTERNET_FLAG_KEEP_CONNECTION,
    1);

if (hResourceHandle == NULL)
{
    InternetCloseHandle(hOpenHandle);
    InternetCloseHandle(hConnectHandle);
    return false;
}

InternetSetOption(hResourceHandle, INTERNET_OPTION_USERNAME, (LPVOID)USERNAME, _tcslen(USERNAME));
InternetSetOption(hResourceHandle, INTERNET_OPTION_PASSWORD, (LPVOID)PASSWORD, _tcslen(PASSWORD));

std::string buf;
if (HttpSendRequest(hResourceHandle, szHeaders, 0, NULL, 0))
{
    while (true)
    {
        std::string part;
        DWORD size;
        if (!InternetQueryDataAvailable(hResourceHandle, &size, 0, 0))break;
        if (size == 0)break;
        part.resize(size);
        if (!InternetReadFile(hResourceHandle, &part[0], part.size(), &size))break;
        if (size == 0)break;
        part.resize(size);
        buf.append(part);
    }
}

if (!buf.empty())
{
    // Get data back
}

InternetCloseHandle(hResourceHandle);
InternetCloseHandle(hConnectHandle);
InternetCloseHandle(hOpenHandle);

Cela devrait fonctionner sur un environnement API Win32.

Voici un exemple .

Michael Haephrati
la source
Que dois-je mettre pour la passerelle? Il n'y a pas de foutue passerelle ... Win API est tellement mauvais.
Tomáš Zato - Réintègre Monica
1
"Gateway" est juste un mot générique pour l'URI ( en.wikipedia.org/wiki/Uniform_Resource_Identifier ) fourni par le fournisseur de services. Cela n'a rien à voir avec Windows.
Michael Haephrati
Ah merci. Je n'ai jamais entendu cette expression être utilisée pour URL, donc cela m'a un peu dérouté. Merci pour la clarification.
Tomáš Zato - Réintègre Monica
Ok, j'ai testé le code et votre exemple ne correspond pas. InternetConnectrenvoie null lorsque l'URL complète est donnée, mais renvoie une valeur non nulle lorsque seul le nom de domaine est fourni. Alors, quand / où utiliser l'URL complète pour obtenir la page que je souhaite télécharger?
Tomáš Zato - Réintègre Monica
Utilisez InternetOpenUrl () au lieu d'InternetConnect () si vous souhaitez utiliser l'URL
Al Po
4

Réponse mise à jour pour avril 2020:

J'ai eu beaucoup de succès, récemment, avec cpp-httplib (en tant que client et serveur). Il est mature et son RPS à filetage unique approximatif est d'environ 6k.

De plus, il existe un cadre très prometteur, cpv-framework , qui peut obtenir environ 180k RPS sur deux cœurs (et évoluera bien avec le nombre de cœurs car il est basé sur le cadre seastar , qui alimente les bases de données les plus rapides sur la planète, scylladb ).

Cependant, le framework cpv est encore relativement immature; donc, pour la plupart des utilisations, je recommande fortement cpp-httplib.

Cette recommandation remplace ma réponse précédente (il y a 8 ans).

Homer6
la source
Merci, essayez-le peut-être dans un avenir proche;)
Hack06
J'aime vraiment l'approche à 1 fichier (5K lignes est ok) de cpp-httplib. Vous avez une idée de ses performances?
Hack06
1
@ Hack06 La référence approximative était d'environ 6000 requêtes par seconde (RPS).
Homer6
3

C et C ++ n'ont pas de bibliothèque standard pour HTTP ou même pour les connexions socket. Au fil des ans, certaines bibliothèques portables ont été développées. Le plus largement utilisé, comme d'autres l'ont dit, est libcurl .

Voici une liste d'alternatives à libcurl (provenant du site Web de libcurl).

De plus, pour Linux, il s'agit d'un simple client HTTP. Vous pouvez implémenter votre propre client HTTP GET simple, mais cela ne fonctionnera pas s'il y a une authentification ou des redirections impliquées ou si vous devez travailler derrière un proxy. Pour ces cas, vous avez besoin d'une bibliothèque complète comme libcurl.

Pour le code source avec libcurl, c'est le plus proche de ce que vous voulez (libcURL a beaucoup d' exemples ). Regardez la fonction principale. Le contenu html sera copié dans le tampon, après une connexion réussie. Remplacez simplement parseHtml par votre propre fonction.

kgiannakakis
la source
3

Vous pouvez utiliser la bibliothèque embeddedRest . Il s'agit d'une bibliothèque légère uniquement en-tête. Il est donc facile de l'inclure dans votre projet et il ne nécessite pas de compilation car il ne contient aucun .cppfichier.

Exemple de demande readme.mdde 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 = "<<response.statusCode()<<", body = *"<<response.body()<<"*"<<endl;
}else{
  cout<<"status code = "<<response.statusCode()<<", description = "<<response.statusDescription()<<endl;
}
fnc12
la source
Ne compile pas sur Win32: /
uhfocuz
@uhfocuz la lib est écrite pour iOS et Android. Mais je peux vous aider à le compiler pour Win32. Ce n'est pas trop difficile
fnc12
J'adore à quel point il est léger pour mon usage, il se compile très bien sur mon Mac mais sur Windows les bibliothèques sont différentes comme vous n'en avez pas netdb.hetc donc je voudrais de l'aide oui
uhfocuz
@uhfocuz tout ce que vous avez à faire est d'ajouter des conditions comme #ifdef _WIN32et d'y ajouter du code spécifique à Windows. Regardez ici - il n'y a pas beaucoup de différence entre les sockets unix et les sockets windows. Je vois deux différences principales: 1) appeler d' WSAStartupabord et 2) utiliser closesocketau lieu declose
fnc12
@uhfocuz veuillez créer un problème dans mon référentiel - j'ajouterai le support win32 une fois que j'aurai assez de temps
fnc12
3

Le protocole HTTP est très simple, il est donc très simple d'écrire un client HTTP. En voici un

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

Il utilise HTTP GET pour récupérer un fichier à partir d'un serveur Web, le serveur et le fichier étant des paramètres de ligne de commande. Le fichier distant est enregistré sur une copie locale.

Avertissement: je suis l'auteur

MODIFIER: URL modifiée

Pedro Vicente
la source
L'URL donnée n'est pas valide.
Shravan40
3

Notez que cela ne nécessite pas libcurl, Windows.h ou WinSock! Aucune compilation de bibliothèques, aucune configuration de projet, etc. J'ai ce code qui fonctionne dans Visual Studio 2017 c ++ sur Windows 10:

#pragma comment(lib, "urlmon.lib")

#include <urlmon.h>
#include <sstream>

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();

Je viens de comprendre comment faire cela, car je voulais un script d'accès API simple, des bibliothèques comme libcurl me causaient toutes sortes de problèmes (même lorsque j'ai suivi les instructions ...), et WinSock est tout simplement trop bas et compliqué .

Je ne suis pas tout à fait sûr de tout le code de lecture IStream (en particulier la condition while - n'hésitez pas à corriger / améliorer), mais bon, cela fonctionne , sans tracas! (Il est logique pour moi que, puisque j'ai utilisé un appel de blocage (synchrone) , cela ne pose aucun problème , ce bytesReadserait toujours> 0U jusqu'à ce que le flux ( ISequentialStream ?) Soit terminé en lecture, mais qui sait.)

Voir aussi: Monikers URL et référence de protocole enfichable asynchrone

Andrew
la source
Il a été édité, mais après une bonne expérience en c ++, je vais maintenir mon affirmation originale selon laquelle c'est probablement la façon la plus simple de faire ce genre de chose en c ++ ... (à moins pour l'instant ...)
Andrew
Je viens de tester URLOpenBlockingStream avec quelques URL de badssl.com (assez pratique), et cette opération échouera si le certificat SSL est mauvais. Dans tous les cas que j'ai testés (quelques-uns seulement), la sortie du code ci-dessus sera une chaîne vide (pas de données de flux). C'est donc plutôt sympa.
Andrew
Je suis d'accord, c'est la facilité, mais comment obtiendriez-vous le code de réponse http?
Robin
@Robin Ha! Vous vouliez le message et le code de réponse?! Vous le savez aussi bien que moi, sauf que peut-être que si vous regardez la documentation et regardez dans les trucs de monikers URL plus manuels, vous pourriez trouver une réponse. Il me semble me rappeler que quelqu'un a posté du code en ligne implémentant manuellement URLOpenBlockingStream, ce qui permettrait une plus grande configuration. Faites-moi savoir si vous comprenez quelque chose!
Andrew
Il y a aussi ceci que je viens de trouver: docs.microsoft.com/en-us/windows/desktop/WinInet/… Je n'ai aucune idée si c'est bon.
Andrew
2

Voici du code C ++ 11 (relativement) simple qui utilise libCURL pour télécharger le contenu d'une URL dans un std::vector<char>:

http_download.hh

# pragma once

#include <string>
#include <vector>

std::vector<char> download(std::string url, long* responseCode = nullptr);

http_download.cc

#include "http_download.hh"

#include <curl/curl.h>
#include <sstream>
#include <stdexcept>

using namespace std;

size_t callback(void* contents, size_t size, size_t nmemb, void* user)
{
  auto chunk = reinterpret_cast<char*>(contents);
  auto buffer = reinterpret_cast<vector<char>*>(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<char> download(string url, long* responseCode)
{
  vector<char> 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);
}
Drew Noakes
la source
2

En général, je recommanderais quelque chose de multiplateforme comme cURL, POCO ou Qt. Cependant, voici un exemple Windows!:

#include <atlbase.h>
#include <msxml6.h>
#include <comutil.h> // _bstr_t

HRESULT hr;
CComPtr<IXMLHTTPRequest> request;

hr = request.CoCreateInstance(CLSID_XMLHTTP60);
hr = request->open(
    _bstr_t("GET"),
    _bstr_t("https://www.google.com/images/srpr/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();
Peter Tseng
la source
2

Si vous recherchez une bibliothèque client HTTP en C ++ prise en charge sur plusieurs plates-formes (Linux, Windows et Mac) pour la consommation de services Web Restful. Vous pouvez avoir les options ci-dessous.

  1. Bibliothèque réseau QT - Permet à l'application d'envoyer des demandes réseau et de recevoir des réponses
  2. C ++ REST SDK - Une nouvelle bibliothèque HTTP tierce avec prise en charge PPL
  3. Libcurl - C'est probablement l'un des http lib les plus utilisés dans le monde natif.
Surbhi Gupta
la source
1

Bien qu'un peu tard. Vous pouvez préférer https://github.com/Taymindis/backcurl .

Il vous permet de faire des appels http sur le développement mobile c ++. Convient pour le développement de jeux mobiles

bcl::init(); // init when using

bcl::execute<std::string>([&](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<std::string>()->c_str());
    printf("Sync === %s\n", ret.c_str());
});


bcl::cleanUp(); // clean up when no more using
minika woon
la source
0

Pour ce faire, vous pouvez utiliser ACE:

#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;
}
inbaly
la source