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

258

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

Sam152
fonte
1
Não, atualmente não há suporte interno, seja por meio do idioma ou da biblioteca padrão para rede. No entanto, há um TS N4370 para rede . Eu também respondi a essa pergunta, pois atrai recomendações da biblioteca.
E o BoostBeast?
twocrush 27/09/18

Respostas:

250

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

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

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

Veja o examplesdiretório na distribuição de fontes curlpp , existem muitos casos mais complexos, bem como um mínimo completo e simples usando curlpp.

meus 2 centavos ...

neuro
fonte
1
a última versão parece estar quebrada no mac .. algo está errado com o config.h quando vinculado como biblioteca.
Eugene
1
Bem, não pude compilar o que foi dito acima. No entanto, a substituição os << myRequest.perform();por myRequest.setOpt( new curlpp::options::WriteStream( &os ) ); myRequest.perform();deu resultados. Certifique-se de não usar http://example.com, isso retornará uma página vazia. Melhor uso, por exemplo http://www.wikipedia.org.
Zane
4
Como você cria o curlpp no ​​MSVS? Não consigo fazê-lo funcionar :(
mr5
2
Não concordo com a edição mais recente de @ ryan-sam. Era claramente a intenção do autor escrever "webdav" e não o desenvolvimento da web, pois a biblioteca fornecida é explicitamente feita para "operações HTTP e WebDAV".
Bostrot
2
@ Bostrot: Sim, o que eu quis dizer. Reverti e adicionei um link, acho que as pessoas pensaram que eu escrevi webdev. Que pena :)
neuro
115

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

Aqui está uma implementação muito melhor:

#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);
}
Designer de software
fonte
Tentei esse código em um Windows Vista compilando com o Dev-C ++ versão 4.9.9.2. Dei-me um monte de erros ao vincular: [Erro do vinculador] referência indefinida ao `WSAStartup @ 8 '
Expanding-Dev
4
@ Somente desenvolvedor de expansão MSVC (visual studio) entende "comentário pragma". Se você usar qualquer outra coisa, deverá vincular "ws2_32.lib" manualmente (como qualquer outra biblioteca).
Navin
24
@JuanLuisSoldi Eu acho que você realmente precisa ser um dev Windows para apreciar a "beleza" deste código ...
static_rtti
O que deve ser recebido (usando recv) aqui? Estou recebendo muita bobagem como saída. Além disso, por que você colocou o que fez no buffer de envio (por exemplo GET / HTTP/1.1.1/... etc)? Como descubro como formatar o que envio?
LazerSharks
43

Atualização 2020: Tenho uma nova resposta que substitui essa, agora com 8 anos, uma: https://stackoverflow.com/a/61177330/278976

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

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

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

O POCO é gratuito, de código aberto (licença de reforço). E não, eu não tenho nenhuma afiliação com a empresa; Eu realmente gosto das interfaces deles. Excelente trabalho pessoal (e moças).

https://pocoproject.org/download.html

Espero que isso ajude alguém ... Levei três dias para experimentar todas essas bibliotecas.

Homer6
fonte
1
Aqui está um exemplo adicional: github.com/pocoproject/poco/blob/develop/Net/samples/httpget/…
Homer6
2
Acabei de baixar o Poco por sua sugestão. Eu preferiria algo leve que se baseia no STL e estimule a reescrever muito dele. Além disso, eu não sou fã do CppUnit e, em particular, de testes de ódio em execução com a compilação, e não espero ter que testar sua biblioteca enquanto eu a construo.
CashCow
É um pouco grande. No entanto, você pode desativar a criação de testes e amostras (ou bibliotecas compartilhadas) com o configure (por exemplo, --no-tests ou --no-samples ou --no-sharedlibs). Veja github.com/pocoproject/poco/blob/develop/configure
Homer6
obrigado por isso. Quero assim mesmo, pois me importo em concluir as tarefas que preciso executar. E notei que eles também têm análise JSON, o que é bom, pois precisarei fazer isso depois de enviar a solicitação HTTP, para a qual obtive a biblioteca.
Cashcow
isso foi há um tempo atrás, mas isso é apenas para informar que nenhum desses links está funcionando agora, até mesmo o repositório do github parece removido ...
Hack06
33

Está sendo desenvolvido um wrapper de ondulação mais novo e menos maduro, chamado C ++ Requests . Aqui está uma solicitação GET simples:

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

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

Disclaimer: Eu sou o mantenedor desta biblioteca .

huu
fonte
10
Eu estava na sua palestra relâmpago no CppCon 2015 ontem. Bem feito - tanto a conversa quanto a biblioteca. Eu particularmente gosto da filosofia de design "Curl for people".
U007D
Olá, acabei de encontrar este post aqui, procurando solicitações HTTP C ++ mais fáceis do que o simples. No entanto, não tenho muita experiência com bibliotecas e não sei como incluir isso no meu projeto C ++ do visual studio. Existe alguma explicação em algum lugar? Sinto que não é específico da biblioteca, mas sim que realmente não sei o que fazer com o que tenho diante de mim agora em geral.
Sossenbinder
2
@Sossenbinder, se você puder se familiarizar com o CMake, poderá gerar arquivos de compilação do Visual Studio para este projeto usando isso. O arquivo de configuração do fornecedor contém um exemplo aproximado de como fazer isso.
huu
2
Parece bom, mas a construção é o inferno, para que o seu lib é inútil, eu não posso confiar em gerenciador de pacotes (necessidade maneira confiável como adicionar deps externamente) e precisa de lib funcional o mais cedo possível ...
dev1223
É assim que se faz. quando você comparar isso com as 200 linhas da resposta segundo mais upvoted .......
v.oddou
17

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

Instale "libcurl" na sua máquina yum install libcurl libcurl-develou equivalente.

Exemplo de uso:

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

Implementação 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
fonte
16

libCURL é uma opção muito boa para você. Dependendo do que você precisa fazer, o tutorial deve dizer o que você deseja, especificamente para o manuseio fácil. Mas, basicamente, você poderia fazer isso apenas para ver a fonte de uma página:

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

Acredito que isso fará com que o resultado seja impresso em stdout. Se você quiser lidar com isso - o que, eu suponho, você precisa - você precisará definir o CURL_WRITEFUNCTION. Tudo isso é abordado no tutorial de ondulação vinculado acima.

Memoria livre
fonte
16

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

Você pode verificar os documentos :

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

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

Marcelo Santos
fonte
4
Acho que o QHttp foi substituído pelo QNetworkAccessManager e classes relacionadas no Qt 4.6 e versões posteriores.
juzzlin
3
QNetworkAccessManagerestá documentado desde o quarto trimestre; e no Qt 4.8 diz: QHttp - This class is obsolete. It is provided to keep old source code working. We strongly advise against using it in new code.Acho que ainda está disponível, se você ignorar os avisos preteridos.
Jesse Chisholm
13

Convém verificar o C ++ REST SDK (codinome "Casablanca"). http://msdn.microsoft.com/en-us/library/jj950081.aspx

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

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

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

óxido de cobre
fonte
10

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

O código do Windows a seguir é testado com o Visual Studio 2013 e Windows 8.1 de 64 bits, além do Windows 7 de 64 bits. Ele terá como alvo uma conexão TCP IPv4 com o 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;
}

Referências:

Descontinuação de gethostbyname

Valor de retorno do soquete ()

Valor de retorno de send ()

Vinz
fonte
7

O C ++ não fornece nenhuma maneira de fazê-lo diretamente. Isso dependeria inteiramente de quais plataformas e bibliotecas você possui.

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

sybreon
fonte
1
Sim - agora, pelo menos. :) stackoverflow.com/a/51959694/1599699
Andrew
@ Andrew: Se o seu "Ele faz" aborda "C ++ do sybreon não fornece nenhuma maneira de fazê-lo diretamente." , a resposta vinculada não é válida, pois mostra uma maneira de fazê-lo usando detalhes específicos do sistema.
Sebastian Mach
@SebastianMach Quero dizer, isso acontece. Basta importar uma biblioteca fornecida pelo sistema e chamar uma função, e ele faz o trabalho para você. Compare isso com todas as outras opções de c ++ e é realmente difícil ou usando código de terceiros. Eu considero isso bem direto.
Andrew Andrew
1
@ Andrew: É "fornecido pelo sistema" apenas no Windows. Em outros sistemas, é diferente. E isso não tem nada a ver com "C ++ não fornece nenhuma maneira de fazê-lo diretamente", o que realmente significa "O padrão C ++ não".
Sebastian Mach
@SebastianMach Sim, mas isso também é subjetivo, porque o c ++ também roda em microcontroladores para tablets, telefones, etc. O OP não disse "padrão c ++", ele apenas disse c ++. Essas respostas estão fornecendo soluções Linux e Windows, porque normalmente é o que você usaria para algo assim. Então, com certeza, não é uma solução Linux, mas sim, é fornecida diretamente por um grande sistema operacional.
Andrew
6

Aqui está um código que funcionará sem a necessidade de usar nenhuma biblioteca de terceiros: primeiro defina seu gateway, usuário, senha e quaisquer outros parâmetros que você precisa enviar para esse servidor específico.

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

Aqui está o próprio código:

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

Isso deve funcionar em um ambiente de API do Win32.

Aqui está um exemplo .

Michael Haephrati
fonte
O que devo colocar como gateway? Não há gateway maldito ... Win API é tão ruim.
Tomáš Zato - Restabelece Monica
1
"Gateway" é apenas uma palavra genérica para o URI ( en.wikipedia.org/wiki/Uniform_Resource_Identifier ) fornecido pelo provedor de serviços. Isso não tem nada a ver com o Windows.
Michael Haephrati
Ah obrigada. Eu nunca ouvi essa expressão ser usada para URL, então isso meio que me confundiu. Obrigado pela clarificação.
Tomáš Zato - Restabelece Monica
Ok, testei o código e seu exemplo não se encaixa. InternetConnectretorna nulo quando o URL completo é fornecido, mas retorna um valor não nulo quando apenas o nome do domínio é fornecido. Então, quando / onde uso o URL completo para obter a página que quero baixar?
Tomáš Zato - Restabelecer Monica
Use InternetOpenUrl () em vez de InternetConnect () se você quiser usar o URL
Al Po
4

Resposta atualizada para abril de 2020:

Recentemente, tive muito sucesso com o cpp-activationplib (como cliente e servidor). É maduro e seu RPS aproximado e de thread único é de cerca de 6k.

Em termos mais avançados , há uma estrutura realmente promissora, a cpv-framework , que pode chegar a cerca de 180k RPS em dois núcleos (e escalará bem com o número de núcleos porque é baseada na estrutura seastar , que fornece os DBs mais rápidos em o planeta, scylladb ).

No entanto, o cpv-framework ainda é relativamente imaturo; portanto, para a maioria dos usos, eu recomendo o cpp-activationplib.

Esta recomendação substitui a minha resposta anterior (há 8 anos).

Homer6
fonte
Obrigado, experimentá-lo, talvez em um futuro próximo;)
Hack06
Eu realmente gosto da abordagem de 1 arquivo (5K linhas está ok) do cpp-httplib. Você tem uma idéia sobre o desempenho dele?
Hack06
1
@ Hack06 O benchmark aproximado foi de cerca de 6000 solicitações por segundo (RPS).
Homer6
3

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

Aqui está uma lista de alternativas para libcurl (provenientes do site da libcurl).

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

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

kgiannakakis
fonte
3

Você pode usar a biblioteca embeddedRest . É uma biblioteca leve apenas de cabeçalho. Portanto, é fácil incluí-lo no seu projeto e não requer compilação, porque não há.cpp arquivos nele.

Solicite um exemplo readme.mddo 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
fonte
Não compila no Win32: /
uhfocuz
@uhfocuz a lib foi escrita para iOS e Android. Mas eu posso ajudá-lo a compilá-lo para o Win32. Não é muito difícil
fnc12
Eu amo como a luz é para meu uso, é compilado muito bem no meu Mac, mas no Windows as bibliotecas são diferentes como você não tem netdb.hetc, então eu gostaria de algumas sim ajuda
uhfocuz
@uhfocuz tudo o que você precisa fazer é adicionar condições como #ifdef _WIN32e adicionar um código específico do Windows. Veja aqui - não há muita diferença entre soquetes unix e soquetes para janelas. Vejo duas diferenças principais: 1) chamar WSAStartupprimeiro e 2) usar closesocket, em vez declose
fnc12
@uhfocuz crie um problema no meu repo - I deve adicionar suporte win32 uma vez que tenho tempo suficiente
fnc12
3

O protocolo HTTP é muito simples, portanto, é muito simples escrever um cliente HTTP. Aqui está um

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

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

Disclaimer: Eu sou o autor

EDIT: URL editado

Pedro Vicente
fonte
O URL fornecido é inválido.
Shravan40
3

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

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

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

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

Não tenho muita certeza sobre todo o código de leitura do IStream (particularmente a condição while - sinta-se à vontade para corrigir / melhorar), mas, ei, funciona , sem complicações! (Faz sentido para mim que, desde que eu usei uma chamada de bloqueio (síncrona) , tudo bem, isso bytesReadsempre seria> 0U até que o fluxo ( ISequentialStream ?) Termine de ser lido, mas quem sabe.

Consulte também: Referência de apelidos de URL e protocolo de conexão assíncrona

Andrew
fonte
Ele foi editado, mas depois de uma boa quantidade de experiência em c ++, defenderei minha afirmação original de que essa é provavelmente a maneira mais fácil de você conseguir fazer esse tipo de coisa em c ++ ... menos por enquanto ...)
Andrew
Acabei de testar o URLOpenBlockingStream com alguns URLs de badssl.com (bastante útil), e essa operação falhará se o certificado SSL estiver ruim. Em todos os casos que testei (apenas alguns), a saída do código acima será uma sequência vazia (sem dados do fluxo). Então isso é muito legal.
Andrew
Concordo que é o easiesst, mas como você obteria o código de resposta http?
Robin
@Robin Ha! Você queria a mensagem e o código de resposta ?! Você sabe tão bem quanto eu, exceto que, talvez, se você olhar a documentação e ler o material mais manual sobre URL Monikers, poderá encontrar uma resposta. Parece que me lembro de alguém postando código on-line implementando manualmente o URLOpenBlockingStream, o que permitiria mais configurações. Deixe-me saber se você descobrir alguma coisa!
Andrew Andrew
Também encontrei o seguinte: docs.microsoft.com/en-us/windows/desktop/WinInet/… Não faço ideia se é bom.
Andrew Andrew
2

Aqui está um código C ++ 11 (relativamente) simples que usa libCURL para baixar o conteúdo de uma URL em 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
fonte
2

Geralmente, eu recomendaria algo entre plataformas, como cURL, POCO ou Qt. No entanto, aqui está um exemplo do 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
fonte
2

Se você estiver procurando por uma biblioteca cliente HTTP em C ++ suportada em várias plataformas (Linux, Windows e Mac) para consumir serviços da Web Restful. Você pode ter as opções abaixo.

  1. Biblioteca de rede QT - Permite que o aplicativo envie solicitações de rede e receba respostas
  2. C ++ REST SDK - Uma biblioteca HTTP de terceiros emergente com suporte a PPL
  3. Libcurl - É provavelmente um dos http lib mais usados ​​no mundo nativo.
Surbhi Gupta
fonte
1

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

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

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

Você pode usar o ACE para fazer isso:

#include "ace/SOCK_Connector.h"

int main(int argc, ACE_TCHAR* argv[])
{
    //HTTP Request Header
    char* szRequest = "GET /video/nice.mp4 HTTP/1.1\r\nHost: example.com\r\n\r\n"; 
    int ilen = strlen(szRequest);

    //our buffer
    char output[16*1024];

    ACE_INET_Addr server (80, "example.com");
    ACE_SOCK_Stream peer;
    ACE_SOCK_Connector connector;
    int ires = connector.connect(peer, server);
    int sum = 0;
    peer.send(szRequest, ilen);
    while (true)
    {
        ACE_Time_Value timeout = ACE_Time_Value(15);
        int rc = peer.recv_n(output, 16*1024, &timeout);
        if (rc == -1)
        {
            break;
        }
        sum += rc;
    }
    peer.close();
    printf("Bytes transffered: %d",sum);

    return 0;
}
inbaly
fonte