¿Cómo se realiza una solicitud HTTP con C ++?

258

¿Hay alguna manera de hacer fácilmente una solicitud HTTP con C ++? Específicamente, quiero descargar el contenido de una página (una API) y verificar el contenido para ver si contiene un 1 o un 0. ¿También es posible descargar el contenido en una cadena?

Sam152
fuente
1
No, actualmente no hay soporte integrado a través del idioma o la biblioteca estándar para redes. Sin embargo, hay un Networking TS N4370 . También escribí esta pregunta en VTC ya que está atrayendo recomendaciones de la biblioteca.
¿Qué hay de BoostBeast?
twocrush

Respuestas:

250

Yo tuve el mismo problema. libcurl está realmente completo. Hay un curlpp de envoltura de C ++ que podría interesarle cuando solicite una biblioteca de C ++. neon es otra biblioteca de C interesante que también admite WebDAV .

curlpp parece natural si usas C ++. Hay muchos ejemplos proporcionados en la distribución fuente. Para obtener el contenido de una URL, haga algo como eso (extraído de ejemplos):

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

Vea el examplesdirectorio en la distribución de fuentes de curlpp , hay muchos casos más complejos, así como uno simple y mínimo completo con curlpp.

mis 2 centavos ...

neuro
fuente
1
la última versión parece estar rota en Mac ... algo está mal con config.h cuando está vinculado como biblioteca.
eugene
1
Bueno, no pude compilar lo anterior. Sin embargo, reemplazar os << myRequest.perform();con myRequest.setOpt( new curlpp::options::WriteStream( &os ) ); myRequest.perform();dio resultados. Asegúrese de no usar http://example.com, esto devolverá una página vacía. Mejor uso, por ejemplo http://www.wikipedia.org.
Zane
44
¿Cómo se construye curlpp en MSVS? No puedo hacerlo funcionar :(
mr5
2
No estoy de acuerdo con la última edición de @ ryan-sam. Claramente, el autor tenía la intención de escribir "webdav" y no el desarrollo web, ya que la biblioteca dada está hecha explícitamente para "operaciones HTTP y WebDAV".
Bostrot
2
@bostrot: Sí, eso es lo que quise decir. Revertí y agregué un enlace, creo que la gente pensó que escribí webdev. Qué pena :)
neuro
115

Código de 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;
}

Aquí hay una implementación mucho mejor:

#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);
}
Diseñador de software
fuente
Intenté este código en una compilación de Windows Vista con Dev-C ++ versión 4.9.9.2. Me dio un montón de errores al vincular: [Error de vinculador] referencia indefinida a `WSAStartup @ 8 '
Expanding-Dev
44
@ Expanding-Dev Only MSVC (estudio visual) entiende el "comentario pragma". Si usa algo más, debe vincular "ws2_32.lib" manualmente (como cualquier otra biblioteca).
Navin
24
@JuanLuisSoldi Creo que realmente necesitas ser un desarrollador de Windows para apreciar la "belleza" de este código ...
static_rtti
¿Qué se supone que debe recibirse (usando recv) aquí? Estoy obteniendo muchas galimatías como resultado. Además, ¿por qué pusiste lo que hiciste en el búfer de envío (por ejemplo GET / HTTP/1.1.1/... etc)? ¿Cómo sé cómo formatear lo que envío?
LazerSharks
43

Actualización 2020: tengo una nueva respuesta que reemplaza esta, ahora de 8 años, una: https://stackoverflow.com/a/61177330/278976

En Linux, probé cpp-netlib, libcurl, curlpp, urdl, boost :: asio y consideré Qt (pero lo rechacé según la licencia). Todos estos estaban incompletos para este uso, tenían interfaces descuidadas, tenían poca documentación, no se mantenían o no admitían https.

Luego, por sugerencia de https://stackoverflow.com/a/1012577/278976 , probé POCO. Wow, desearía haber visto esto hace años. Aquí hay un ejemplo de cómo hacer una solicitud HTTP GET con POCO:

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

POCO es gratis, de código abierto (licencia de refuerzo). Y no, no tengo ninguna afiliación con la empresa; Realmente me gustan sus interfaces. Buen trabajo chicos (y chicas).

https://pocoproject.org/download.html

Espero que esto ayude a alguien ... me tomó tres días probar todas estas bibliotecas.

Homero6
fuente
1
Aquí hay un ejemplo adicional: github.com/pocoproject/poco/blob/develop/Net/samples/httpget/…
Homer6
2
Acabo de descargar Poco por su sugerencia. Preferiría algo ligero que se basa en STL y aumentar en lugar de reescribir gran parte de él. Además, no soy fanático de CppUnit y, en particular, de las pruebas de odio que se ejecutan con la compilación, y no espero tener que probar su biblioteca mientras la construyo.
CashCow
Es un poco grande Sin embargo, puede desactivar la creación de pruebas y muestras (o bibliotecas compartidas) con configure (es decir, --no-tests o --no-samples o --no-sharedlibs). Ver github.com/pocoproject/poco/blob/develop/configure
Homer6
gracias por eso. Lo quiero de todos modos, ya que me importa hacer las tareas que necesito hacer. Y noto que también tienen un análisis JSON, lo cual es bueno, ya que tendré que hacerlo después de enviar la solicitud HTTP, que es para lo que obtuve la biblioteca.
CashCow
fue hace un tiempo, pero esto es solo para hacerle saber que ninguno de esos enlaces están funcionando ahora, incluso el repositorio de github parece eliminado ...
Hack06
33

Se está desarrollando un envoltorio de rizos más nuevo y menos maduro llamado C ++ Requests . Aquí hay una simple solicitud 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;
}

Es compatible con una amplia variedad de verbos HTTP y opciones de rizo. Hay más documentación de uso aquí .

Descargo de responsabilidad: soy el mantenedor de esta biblioteca .

huu
fuente
10
Ayer estuve en su charla de relámpagos de CppCon 2015. Bien hecho, tanto la charla como la biblioteca. Me gusta especialmente la filosofía de diseño "Curl for people".
U007D
Hola, acabo de encontrar esta publicación aquí, en busca de solicitudes HTTP C ++ más fáciles que las simples. Sin embargo, no tengo mucha experiencia con las bibliotecas, y realmente no sé cómo incluir esto en mi proyecto de Visual Studio C ++. ¿Hay alguna explicación en alguna parte? Siento que no es específico de la biblioteca, sino que realmente no sé qué hacer con lo que tengo frente a mí ahora en general.
Sossenbinder
2
@Sossenbinder, si puede familiarizarse con CMake, puede generar archivos de compilación de Visual Studio para este proyecto utilizando eso. El archivo de configuración del transportador contiene un ejemplo aproximado de cómo lograr esto.
huu
2
Se ve bien, pero el edificio es el infierno, por lo que su lib es inútil, no puedo confiar en el gestor de paquetes (necesidad manera fiable cómo agregar deps externamente) y la necesidad lib funcional tan pronto como sea posible ...
dev1223
Asi es como lo haces. cuando compara esto con las 200 líneas de la segunda respuesta más votada .......
v.oddou
17

Aquí está mi envoltorio mínimo alrededor de cURL para poder obtener una página web como una cadena. Esto es útil, por ejemplo, para pruebas unitarias. Básicamente es un contenedor RAII alrededor del código C.

Instale "libcurl" en su máquina yum install libcurl libcurl-develo equivalente.

Ejemplo de uso:

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

Implementación de clase:

#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
fuente
16

libCURL es una muy buena opción para ti. Dependiendo de lo que necesite hacer, el tutorial debe decirle lo que desea, específicamente para el manejo fácil. Pero, básicamente, puedes hacer esto solo para ver el origen de una 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 );

Creo que esto hará que el resultado se imprima en stdout. Si desea manejarlo, lo cual, supongo que sí, debe configurar CURL_WRITEFUNCTION. Todo eso está cubierto en el tutorial de curl vinculado anteriormente.

Memoria libre
fuente
16

Como desea una solución C ++, puede usar Qt . Tiene una clase QHttp que puedes usar.

Puedes consultar los documentos :

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

Qt también tiene mucho más que podrías usar en una aplicación C ++ común.

Marcelo Santos
fuente
44
Creo que QHttp ha sido reemplazado por QNetworkAccessManager y clases relacionadas en Qt 4.6 y versiones posteriores.
juzzlin
3
QNetworkAccessManagerha sido documentado desde Qt 4.4; y en 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.así que supongo que todavía está disponible, si ignoras las advertencias obsoletas.
Jesse Chisholm
13

Es posible que desee comprobar C ++ REST SDK (nombre en clave "Casablanca"). http://msdn.microsoft.com/en-us/library/jj950081.aspx

Con C ++ REST SDK, puede conectarse más fácilmente a los servidores HTTP desde su aplicación C ++.

Ejemplo de uso:

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

El SDK de C ++ REST es un proyecto de Microsoft para la comunicación cliente-servidor basada en la nube en código nativo utilizando un diseño moderno y asíncrono de API C ++.

óxido de cobre
fuente
10

Con esta respuesta, me refiero a la respuesta de Software_Developer . Al reconstruir el código, descubrí que algunas partes están en desuso ( gethostbyname()) o no proporcionan manejo de errores (creación de sockets, envío de algo) para una operación.

El siguiente código de Windows se prueba con Visual Studio 2013 y Windows 8.1 de 64 bits, así como con Windows 7 de 64 bits. Apuntará a una conexión TCP IPv4 con el servidor 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;
}

Referencias

Desuso de gethostbyname

Valor de retorno de socket ()

Valor de retorno de send ()

Vinz
fuente
7

C ++ no proporciona ninguna forma de hacerlo directamente. Dependería por completo de las plataformas y bibliotecas que tenga.

En el peor de los casos, puede usar la biblioteca boost :: asio para establecer una conexión TCP, enviar los encabezados HTTP (RFC 2616) y analizar las respuestas directamente. Mirando las necesidades de su aplicación, esto es bastante simple de hacer.

sybreon
fuente
1
Lo hace, ahora, al menos. :) stackoverflow.com/a/51959694/1599699
Andrew
@Andrew: Si su "Sí lo hace" se dirige a Sybreon "C ++ no proporciona ninguna forma de hacerlo directamente". , entonces la respuesta vinculada no es válida, ya que muestra una manera de hacerlo utilizando los detalles del sistema.
Sebastian Mach
@SebastianMach Quiero decir, sin embargo. Simplemente importe una biblioteca proporcionada por el sistema y llame a una función, y hace el trabajo por usted. Compare eso con todas las otras opciones de c ++ y es realmente difícil o usar código de terceros. Lo considero bastante directo.
Andrew
1
@ Andrew: es "proporcionado por el sistema" solo en Windows. En otros sistemas, es diferente. Y no tiene nada que ver con "C ++ no proporciona ninguna forma de hacerlo directamente", lo que realmente significa "El estándar C ++ no lo hace".
Sebastian Mach
@SebastianMach Sí, pero eso también es subjetivo porque c ++ también se ejecuta en teléfonos, tabletas, microcontroladores, etc. OP no dijo "c ++ standard", solo dijo c ++. Estas respuestas proporcionan soluciones de Linux y Windows, porque eso es lo que normalmente usarías para algo como esto. Entonces, claro, no es una solución de Linux, pero sí, es proporcionada directamente por un sistema operativo principal.
Andrew
6

Aquí hay un código que funcionará sin necesidad de usar ninguna biblioteca de terceros: Primero defina su puerta de enlace, usuario, contraseña y cualquier otro parámetro que necesite enviar a este servidor específico.

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

Aquí está el código en sí:

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

Eso debería funcionar en un entorno API Win32.

Aquí hay un ejemplo .

Michael Haephrati
fuente
¿Qué debo poner para puerta de enlace? No hay maldita puerta de enlace ... Win API es tan malo.
Tomáš Zato - Restablece a Monica
1
"Gateway" es solo una palabra genérica para el URI ( en.wikipedia.org/wiki/Uniform_Resource_Identifier ) provisto por el proveedor de servicios. Esto no tiene nada que ver con Windows.
Michael Haephrati
Ah gracias. Nunca escuché que esa expresión se usara para URL, así que eso me confundió un poco. Gracias por la aclaración.
Tomáš Zato - Restablece a Monica
Ok, probé el código y tu ejemplo no cuadra. InternetConnectdevuelve nulo cuando se proporciona la URL completa, pero devuelve un valor no nulo cuando solo se proporciona el nombre de dominio. Entonces, ¿cuándo / dónde uso la URL completa para obtener la página que quiero descargar?
Tomáš Zato - Restablece a Monica
Use InternetOpenUrl () en lugar de InternetConnect () si desea usar url
Al Po
4

Respuesta actualizada para abril de 2020:

Recientemente tuve mucho éxito con cpp-httplib (tanto como cliente como servidor). Es maduro y su RPS aproximado de un solo subproceso es de alrededor de 6k.

En una parte más innovadora , hay un marco realmente prometedor, cpv-framework , que puede obtener alrededor de 180k RPS en dos núcleos (y escalará bien con la cantidad de núcleos porque se basa en el marco de Seastar , que alimenta los DB más rápidos en El planeta, scylladb ).

Sin embargo, cpv-framework todavía es relativamente inmaduro; por lo tanto, para la mayoría de los usos, recomiendo cpp-httplib.

Esta recomendación reemplaza mi respuesta anterior (hace 8 años).

Homero6
fuente
Gracias, lo intentaré tal vez en el futuro cercano;)
Hack06
Realmente me gusta el enfoque de 1 archivo (5K líneas está bien) de cpp-httplib. ¿Tienes una idea sobre su rendimiento?
Hack06
1
@ Hack06 El punto de referencia aproximado fue de aproximadamente 6000 solicitudes por segundo (RPS).
Homer6
3

C y C ++ no tienen una biblioteca estándar para HTTP o incluso para conexiones de socket. Con los años se han desarrollado algunas bibliotecas portátiles. El más utilizado, como han dicho otros, es libcurl .

Aquí hay una lista de alternativas a libcurl (proveniente del sitio web de libcurl).

Además, para Linux, este es un cliente HTTP simple. Puede implementar su propio cliente HTTP GET simple, pero esto no funcionará si hay autenticación o redireccionamientos involucrados o si necesita trabajar detrás de un proxy. Para estos casos, necesita una biblioteca completa como libcurl.

Para el código fuente con libcurl, este es el más cercano a lo que desea (Libcurl tiene muchos ejemplos ). Mira la función principal. El contenido html se copiará en el búfer, después de una conexión exitosa. Simplemente reemplace parseHtml con su propia función.

kgiannakakis
fuente
3

Puede usar la biblioteca embeddedRest . Es una biblioteca ligera de solo encabezado. Por lo tanto, es fácil incluirlo en su proyecto y no requiere compilación porque no contiene .cpparchivos.

Solicitar ejemplo 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
fuente
No se compila en Win32: /
uhfocuz
@uhfocuz the lib está escrito para iOS y Android. Pero puedo ayudarte a compilarlo para Win32. No es demasiado difícil
fnc12
Me encanta cómo la luz es para mi uso, que compila bien en mi Mac, pero en Windows las librerías son diferentes al igual que usted no tiene netdb.h, etc por lo que me gustaría algunos sí ayuda
uhfocuz
@uhfocuz todo lo que tiene que hacer es agregar condiciones como #ifdef _WIN32y agregar allí el código específico de Windows. Mire aquí : no hay mucha diferencia entre los sockets de Unix y los sockets de Windows. Veo dos diferencias principales: 1) llamar WSAStartupprimero y 2) usar en closesocketlugar declose
fnc12
@uhfocuz por favor crea un problema en mi repositorio - Agregaré soporte win32 una vez que tenga suficiente tiempo
fnc12
3

El protocolo HTTP es muy simple, por lo que es muy simple escribir un cliente HTTP. Acá hay uno

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

Utiliza HTTP GET para recuperar un archivo de un servidor web, tanto el servidor como el archivo son parámetros de línea de comando. El archivo remoto se guarda en una copia local.

Descargo de responsabilidad: soy el autor

EDITAR: URL editada

Pedro Vicente
fuente
La URL dada no es válida.
Shravan40
3

¡Tenga en cuenta que esto no requiere libcurl, Windows.h o WinSock! Sin compilación de bibliotecas, sin configuración de proyecto, etc. Tengo este código funcionando en Visual Studio 2017 c ++ en 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();

Acabo de descubrir cómo hacer esto, ya que quería un script simple de acceso a la API, las bibliotecas como libcurl me estaban causando todo tipo de problemas (incluso cuando seguía las instrucciones ...), y WinSock es demasiado bajo y complicado. .

No estoy muy seguro acerca de todo el código de lectura de IStream (particularmente la condición while, siéntase libre de corregirlo / mejorarlo), pero bueno, ¡ funciona , sin problemas! (Tiene sentido para mí que, dado que utilicé una llamada de bloqueo (sincrónica) , está bien, eso bytesReadsiempre sería> 0U hasta que la secuencia ( ISequentialStream ?) Termine de leerse, pero quién sabe).

Ver también: Monikers de URL y referencia de protocolo conectable asíncrono

Andrés
fuente
Fue editado, pero después de una buena cantidad de experiencia en c ++, voy a mantener mi afirmación original de que esta es probablemente la forma más fácil en la que podrá hacer este tipo de cosas en c ++ ... (En menos por ahora ...)
Andrew
Acabo de probar URLOpenBlockingStream con algunas URL de badssl.com (bastante útil), y esta operación fallará si el certificado SSL es malo. En todos los casos que probé (solo unos pocos), la salida del código anterior será una cadena vacía (sin datos de flujo). Eso es muy bueno.
Andrew
Estoy de acuerdo en que es la más fácil, pero ¿cómo obtendrías el código de respuesta http?
Robin
@Robin Ha! ¿Querías el mensaje y el código de respuesta? Sabes tan bien como yo, excepto que tal vez si miras la documentación y miras en el manual de Monikers de URL más manual, podrías encontrar una respuesta. Me parece recordar que alguien publicó código en línea implementando URLOpenBlockingStream manualmente, lo que permitiría una mayor configuración. ¡Avísame si descubres algo!
Andrew
También hay esto que acabo de encontrar: docs.microsoft.com/en-us/windows/desktop/WinInet/… No tengo idea si es bueno.
Andrew
2

Aquí hay un código C ++ 11 (relativamente) simple que usa libCURL para descargar el contenido de una URL en 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
fuente
2

En general, recomendaría algo multiplataforma como cURL, POCO o Qt. ¡Sin embargo, aquí hay un ejemplo de 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
fuente
2

Si está buscando una biblioteca de cliente HTTP en C ++ que sea compatible con múltiples plataformas (Linux, Windows y Mac) para consumir servicios web de Restful. Puedes tener las siguientes opciones.

  1. QT Network Library : permite que la aplicación envíe solicitudes de red y reciba respuestas
  2. C ++ REST SDK : una biblioteca HTTP emergente de terceros con soporte PPL
  3. Libcurl : es probablemente una de las librerías http más utilizadas en el mundo nativo.
Surbhi Gupta
fuente
1

Aunque un poco tarde. Puede preferir https://github.com/Taymindis/backcurl .

Le permite hacer llamadas http en el desarrollo móvil de c ++. Apto para el desarrollo de juegos móviles

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
fuente
0

Puede usar ACE para hacerlo:

#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
fuente