Come si effettua una richiesta HTTP con C ++?


258

Esiste un modo per effettuare facilmente una richiesta HTTP con C ++? In particolare, voglio scaricare il contenuto di una pagina (un'API) e controllare il contenuto per vedere se contiene un 1 o uno 0. È anche possibile scaricare il contenuto in una stringa?


1
No, al momento non esiste alcun supporto integrato tramite la lingua o la libreria standard per il networking. Tuttavia, esiste un Networking TS N4370 . Ho anche fatto VTC questa domanda poiché sta attirando consigli sulle biblioteche.

Che ne dici di BoostBeast?
twocrush,

Risposte:


250

Ho avuto lo stesso problema. libcurl è davvero completo. Esiste un curlpp wrapper C ++ che potrebbe interessarti quando chiedi una libreria C ++. neon è un'altra interessante libreria C che supporta anche WebDAV .

curlpp sembra naturale se usi C ++. Ci sono molti esempi forniti nella distribuzione di origine. Per ottenere il contenuto di un URL, fai qualcosa del genere (estratto dagli esempi):

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

Vedi la examplesdirectory nella distribuzione dei sorgenti di curlpp , ci sono molti casi più complessi, oltre a un semplice minimo completo usa curlpp.

i miei 2 centesimi ...


1
l'ultima versione sembra essere rotta sotto mac .. qualcosa è confuso con config.h quando è collegato come libreria.
Eugene,

1
Bene, non ho potuto compilare quanto sopra. Tuttavia, sostituendo os << myRequest.perform();con ha myRequest.setOpt( new curlpp::options::WriteStream( &os ) ); myRequest.perform();dato risultati. Assicurati di non usare http://example.com, questo restituirà una pagina vuota. Migliore utilizzo ad es http://www.wikipedia.org.
Zane,

4
Come si crea curlpp in MSVS? Non riesco a farlo funzionare :(
mr5

2
Non sono d'accordo con l'ultima modifica di @ ryan-sam. Era chiaramente intenzione dell'autore scrivere "webdav" e non lo sviluppo web, dato che la libreria data è stata creata esplicitamente per "operazioni HTTP e WebDAV".
Bostrot,

2
@bostrot: Sì, quello che volevo dire. Ho ripristinato e aggiunto un collegamento, penso che la gente pensasse di aver scritto webdev. Che peccato :)
Neuro,

115

Codice di 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;
}

Ecco un'implementazione molto migliore:

#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);
}

Ho provato questo codice su una compilazione di Windows Vista con Dev-C ++ versione 4.9.9.2. Mi hanno dato un sacco di errori durante il collegamento: [errore Linker] riferimento indefinito a `WSAStartup @ 8 '
Expanding-Dev

4
@ Expanding-Dev Solo MSVC (visual studio) comprende "commento pragma". Se usi qualcos'altro devi collegare manualmente "ws2_32.lib" (come qualsiasi altra libreria).
Navin,

24
@JuanLuisSoldi Immagino che tu abbia davvero bisogno di essere uno sviluppatore di Windows per apprezzare la "bellezza" di questo codice ...
static_rtti

Cosa dovrebbe essere ricevuto (usando recv) qui? Sto ottenendo un sacco di cose incomprensibili come output. Inoltre, perché hai inserito ciò che hai fatto nel buffer di invio (ad es. GET / HTTP/1.1.1/... etc)? Come faccio a sapere come formattare ciò che invio?
LazerSharks,

43

Aggiornamento 2020: ho una nuova risposta che sostituisce questa, ora di 8 anni, una: https://stackoverflow.com/a/61177330/278976

Su Linux, ho provato cpp-netlib, libcurl, curlpp, urdl, boost :: asio e ho considerato Qt (ma ho rifiutato in base alla licenza). Tutti questi erano incompleti per questo uso, avevano interfacce sciatte, scarsa documentazione, non mantenuti o non supportavano https.

Quindi, su suggerimento di https://stackoverflow.com/a/1012577/278976 , ho provato POCO. Wow, vorrei averlo visto anni fa. Ecco un esempio di come effettuare una richiesta GET HTTP con POCO:

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

POCO è gratuito, open source (licenza boost). E no, non ho alcuna affiliazione con l'azienda; Mi piacciono molto le loro interfacce. Ottimo lavoro ragazzi (e ragazze).

https://pocoproject.org/download.html

Spero che questo aiuti qualcuno ... mi ci sono voluti tre giorni per provare tutte queste librerie.



2
Ho appena scaricato Poco su tuo suggerimento. Preferirei qualcosa di leggero che si basa su STL e aumenti piuttosto che riscriverne gran parte. Inoltre non sono un fan di CppUnit e in particolare dei test di odio in esecuzione con la build e non mi aspetto di dover testare la loro libreria mentre la costruisco.
CashCow,

È un po 'grande. Tuttavia, puoi disabilitare la creazione di test ed esempi (o librerie condivise) con configure (es. --No-test o --no-samples o --no-sharedlibs). Vedi github.com/pocoproject/poco/blob/develop/configure
Homer6

grazie per questo. Lo voglio comunque perché mi preoccupo di svolgere i compiti che devo svolgere. E noto che hanno anche JSON che analizza lì, il che è buono in quanto dovrò farlo dopo aver inviato la richiesta HTTP che è ciò per cui ho ottenuto la libreria.
CashCow,

è stato un po 'di tempo fa, ma questo è solo per farti sapere che nessuno di quei link funziona ora, anche il repository github sembra rimosso ...
Hack06

33

È in fase di sviluppo un wrapper per arricciatura più nuovo e meno maturo chiamato Richieste C ++ . Ecco una semplice richiesta 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;
}

Supporta un'ampia varietà di verbi HTTP e opzioni di arricciatura. C'è più documentazione d'uso qui .

Disclaimer: sono il manutentore di questa biblioteca .


10
Ieri ero al tuo fulmine di CppCon 2015. Ben fatto - sia il discorso che la biblioteca. Mi piace particolarmente la filosofia del design "Curl for people".
U007D,

Ciao, ho appena trovato questo post qui, alla ricerca di richieste HTTP C ++ più semplici rispetto al modo semplice. Tuttavia, non ho molta esperienza con le biblioteche e non so davvero come includerlo nel mio progetto C ++ in Visual Studio. C'è qualche spiegazione da qualche parte? Sento che non è specifico per la biblioteca, ma piuttosto che non so davvero cosa fare con quello che ho davanti a me ora in generale.
Sossenbinder,

2
@Sossenbinder, se puoi familiarizzare con CMake, puoi generare i file di build di Visual Studio per questo progetto usando quello. Il file di configurazione dell'appveyor contiene un esempio approssimativo di come eseguire ciò.
huu

2
Sembra carino, ma costruire è un inferno, quindi la tua lib è inutile, non posso fare affidamento sul gestore pacchetti (ho bisogno di un modo affidabile per aggiungere deps esternamente) e ho bisogno di una lib funzionale il più presto possibile ...
dev1223

è così che lo fai. quando lo confronti con le 200 righe della seconda risposta più votata .......
v.oddou,

17

Ecco il mio wrapper minimo attorno a cURL per essere in grado di recuperare una pagina Web come stringa. Ciò è utile, ad esempio, per i test unitari. Fondamentalmente è un wrapper RAII attorno al codice C.

Installa "libcurl" sul tuo computer yum install libcurl libcurl-develo equivalente.

Esempio di utilizzo:

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

Implementazione della 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;
    }
};

16

libCURL è una buona opzione per te. A seconda di ciò che devi fare, il tutorial dovrebbe dirti quello che vuoi, in particolare per la facile gestione. Ma, fondamentalmente, potresti farlo solo per vedere la fonte di una pagina:

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

Credo che questo causerà la stampa del risultato su stdout. Se invece vuoi gestirlo, cosa che, suppongo, lo fai, devi impostare CURL_WRITEFUNCTION. Tutto ciò è trattato nel tutorial sull'arricciatura linkato sopra.


16

Come vuoi una soluzione C ++, puoi usare Qt . Ha una classe QHttp che puoi usare.

Puoi controllare i documenti :

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

Qt ha anche molto di più che potresti usare in un'app C ++ comune.


4
Penso che QHttp sia stato sostituito con QNetworkAccessManager e le classi correlate in Qt 4.6 e versioni successive.
juzzlin,

3
QNetworkAccessManagerè stato documentato dal Qt 4.4; e in Qt 4.8 dice: QHttp - This class is obsolete. It is provided to keep old source code working. We strongly advise against using it in new code.Quindi immagino che sia ancora disponibile, se si ignorano gli avvisi deprecati.
Jesse Chisholm,

13

Si consiglia di controllare l' SDK REST C ++ (nome in codice "Casablanca"). http://msdn.microsoft.com/en-us/library/jj950081.aspx

Con l'SDK REST C ++, puoi connetterti più facilmente ai server HTTP dalla tua app C ++.

Esempio di utilizzo:

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

L'SDK REST C ++ è un progetto Microsoft per la comunicazione client-server basata su cloud in codice nativo che utilizza un moderno design API C ++ asincrono.


10

Con questa risposta mi riferisco alla risposta di Software_Developer . Ricostruendo il codice ho scoperto che alcune parti sono obsolete ( gethostbyname()) o non forniscono la gestione degli errori (creazione di socket, invio di qualcosa) per un'operazione.

Il seguente codice di Windows viene testato con Visual Studio 2013 e Windows 8.1 a 64 bit e Windows 7 a 64 bit. Mirerà a una connessione TCP IPv4 con il Web Server di 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;
}

Riferimenti:

Deprecazione di gethostbyname

Valore di ritorno del socket ()

Valore di ritorno di send ()


7

C ++ non fornisce alcun modo per farlo direttamente. Dipenderebbe interamente da quali piattaforme e librerie hai.

Nel peggiore dei casi, è possibile utilizzare la libreria boost :: asio per stabilire una connessione TCP, inviare le intestazioni HTTP (RFC 2616) e analizzare direttamente le risposte. Osservando le esigenze della tua applicazione, questo è abbastanza semplice da fare.


1
Lo fa - almeno adesso. :) stackoverflow.com/a/51959694/1599699
Andrew,

@Andrew: Se il tuo "It" si rivolge al sybreon "Il C ++ non fornisce alcun modo per farlo direttamente". , quindi la risposta collegata non è valida in quanto mostra un modo per farlo utilizzando le specifiche di sistema.
Sebastian Mach,

@SebastianMach Voglio dire, lo fa però. Basta importare una libreria fornita dal sistema e chiamare una funzione, e fa il lavoro per te. Confrontalo con tutte le altre opzioni di c ++ ed è davvero difficile o usa un codice di terze parti. Lo considero piuttosto diretto.
Andrew,

1
@Andrew: è "fornito dal sistema" solo su Windows. Su altri sistemi, è diverso. E non ha nulla a che fare con "C ++ non fornisce alcun modo per farlo direttamente", il che significa che "lo standard C ++ non lo fa".
Sebastian Mach,

@SebastianMach Sì, ma è anche soggettivo perché c ++ funziona anche su telefoni tablet microcontrollori ecc. Se non ogni singolo dispositivo o sistema operativo supporta facilmente alcune funzionalità in c ++, lo chiamiamo non fornito direttamente da c ++? OP non ha detto "standard c ++", ha solo detto c ++. Queste risposte forniscono soluzioni Linux e Windows, perché in genere è quello che useresti per qualcosa del genere. Quindi certo, non è una soluzione Linux, ma sì, è fornita direttamente da un sistema operativo principale.
Andrew,

6

Ecco del codice che funzionerà senza la necessità di utilizzare alcuna libreria di terze parti: per prima cosa definisci il tuo gateway, utente, password e tutti gli altri parametri che devi inviare a questo specifico server.

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

Ecco il codice stesso:

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

Dovrebbe funzionare su un ambiente API Win32.

Ecco un esempio .


Cosa devo mettere per gateway? Non esiste un dannato gateway ... Win API è così male.
Tomáš Zato - Ripristina Monica

1
"Gateway" è solo una parola generica per l'URI ( en.wikipedia.org/wiki/Uniform_Resource_Identifier ) fornito dal fornitore di servizi. Questo non ha nulla a che fare con Windows.
Michael Haephrati,

Ah grazie. Non ho mai sentito quell'espressione da usare per l'URL, quindi mi ha confuso. Grazie per il chiarimento.
Tomáš Zato - Ripristina Monica

Ok, ho testato il codice e il tuo esempio non si somma. InternetConnectrestituisce null quando viene fornito l'URL completo, ma restituisce un valore non null quando viene fornito solo il nome di dominio. Quindi quando / dove posso utilizzare l'URL completo per ottenere la pagina che voglio scaricare?
Tomáš Zato - Ripristina Monica

Usa InternetOpenUrl () invece di InternetConnect () se vuoi usare url
Al Po

4

Risposta aggiornata per aprile 2020:

Di recente ho avuto molto successo con cpp-httplib (sia come client che come server). È maturo e il suo RPS approssimativo a thread singolo è di circa 6k.

Su più margini, c'è un framework davvero promettente, cpv-framework , che può ottenere circa 180k RPS su due core (e si ridimensionerà bene con il numero di core perché è basato sul framework seastar , che alimenta i DB più veloci su il pianeta, scylladb ).

Tuttavia, cpv-framework è ancora relativamente immaturo; quindi, per la maggior parte degli usi, consiglio vivamente cpp-httplib.

Questa raccomandazione sostituisce la mia risposta precedente (8 anni fa).


Grazie, lo proverò forse nel prossimo futuro;)
Hack06

Mi piace molto l'approccio a 1 file (5K righe è ok) di cpp-httplib. Hai un'idea delle sue prestazioni?
Hack06,

1
@ Hack06 Il benchmark approssimativo era di circa 6000 richieste al secondo (RPS).
Homer6

3

C e C ++ non hanno una libreria standard per HTTP o nemmeno per connessioni socket. Nel corso degli anni sono state sviluppate alcune librerie portatili. Il più usato, come altri hanno già detto, è libcurl .

Ecco un elenco di alternative a libcurl (proveniente dal sito web di libcurl).

Inoltre, per Linux, questo è un semplice client HTTP. Potresti implementare il tuo semplice client HTTP GET, ma questo non funzionerà se ci sono autenticazioni o reindirizzamenti coinvolti o se devi lavorare dietro un proxy. Per questi casi è necessaria una libreria completa come libcurl.

Per il codice sorgente con libcurl, questo è il più vicino a quello che vuoi (Libcurl ha molti esempi ). Guarda la funzione principale. Il contenuto html verrà copiato nel buffer, dopo una connessione riuscita. Basta sostituire parseHtml con la tua funzione.


3

È possibile utilizzare la libreria embeddedRest . È una libreria leggera solo per le intestazioni. Quindi è facile includerlo nel tuo progetto e non richiede compilazione perché no.cpp file.

Richiedi un esempio readme.mddal repository:

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

Non compilare su Win32: /
uhfocuz il

@uhfocuz the lib è scritto per iOS e Android. Ma posso aiutarti a compilarlo per Win32. Non è troppo difficile
fnc12

Adoro quanto sia leggero per il mio uso, è stato compilato perfettamente sul mio Mac ma su Windows le librerie sono diverse come non hai netdb.hecc. Quindi mi piacerebbe un po 'di aiuto sì
uhfocuz

@uhfocuz tutto quello che devi fare è aggiungere condizioni come #ifdef _WIN32e aggiungere lì il codice specifico di Windows. Guarda qui - non c'è molta differenza tra socket unix e socket windows. Vedo due differenze principali: 1) chiama WSAStartupprima e 2) usa closesocketinvece diclose
fnc12

@uhfocuz, crea un problema nel mio repository - aggiungerò il supporto win32 una volta che avrò abbastanza tempo
fnc12

3

Il protocollo HTTP è molto semplice, quindi è molto semplice scrivere un client HTTP. Eccone uno

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

Utilizza HTTP GET per recuperare un file da un server Web, sia il server che il file sono parametri della riga di comando. Il file remoto viene salvato in una copia locale.

Disclaimer: sono l'autore

EDIT: URL modificato


L'URL indicato non è valido.
Shravan40,

3

Nota che questo non richiede libcurl, Windows.h o WinSock! Nessuna compilazione di librerie, nessuna configurazione di progetto, ecc. Ho questo codice che funziona in Visual Studio 2017 c ++ su 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();

Ho appena capito come fare, poiché volevo un semplice script di accesso all'API, librerie come libcurl mi stavano causando tutti i tipi di problemi (anche quando ho seguito le istruzioni ...) e WinSock è troppo basso e complicato .

Non sono abbastanza sicuro di tutto il codice di lettura IStream (in particolare la condizione while - sentiti libero di correggere / migliorare), ma ehi, funziona , senza problemi! (Per me ha senso che, dal momento che ho usato una chiamata di blocco (sincrona) , va bene, bytesReadsarebbe sempre> 0U fino a quando il flusso ( ISequentialStream ?) Non sarà letto, ma chi lo sa.)

Vedi anche: URL Monikers e Asynchronous Pluggable Protocol Reference


È stato modificato, ma dopo una discreta esperienza in c ++, sopporterò la mia affermazione originale che questo è probabilmente il modo più semplice in cui sarai mai in grado di fare questo tipo di cose in c ++ ... (At almeno per ora ...)
Andrew,

Ho appena testato URLOpenBlockingStream con alcuni URL di badssl.com (abbastanza utile) e questa operazione fallirà se il certificato SSL non è valido. In ogni caso che ho provato (solo alcuni), l'output del codice sopra sarà una stringa vuota (nessun dato di flusso). Quindi è carino.
Andrew,

Concordo sul fatto che sia facile, ma come otterresti il ​​codice di risposta http?
Robin,

@Robin Ha! Volevi il messaggio e il codice di risposta ?! Sai come lo so io, tranne per il fatto che forse se guardi la documentazione e guardi le cose più manuali di Monikers nell'URL potresti trovare una risposta. Mi sembra di ricordare a qualcuno che pubblica codice online implementando manualmente URLOpenBlockingStream, il che consentirebbe una maggiore configurazione. Fammi sapere se trovi qualcosa!
Andrew,

C'è anche questo che ho appena trovato: docs.microsoft.com/en-us/windows/desktop/WinInet/… Non ho idea se va bene.
Andrew,

2

Ecco un codice (relativamente) semplice C ++ 11 che utilizza libCURL per scaricare il contenuto di un URL in a 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);
}

2

Generalmente consiglierei qualcosa di multipiattaforma come cURL, POCO o Qt. Tuttavia, ecco un esempio di 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();

2

Se stai cercando una libreria client HTTP in C ++ supportata su più piattaforme (Linux, Windows e Mac) per l'utilizzo di servizi Web Restful. Puoi avere le seguenti opzioni.

  1. Libreria di rete QT : consente all'applicazione di inviare richieste di rete e ricevere risposte
  2. C ++ REST SDK - Una libreria HTTP di terze parti emergente con supporto PPL
  3. Libcurl - È probabilmente uno dei lib lib http più utilizzati nel mondo nativo.

1

Anche se un po 'in ritardo. Puoi preferire https://github.com/Taymindis/backcurl .

Ti consente di effettuare chiamate http sullo sviluppo di c ++ mobile. Adatto allo sviluppo di giochi per dispositivi mobili

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

0

È possibile utilizzare ACE per fare ciò:

#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;
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.