Jak złożyć zapytanie HTTP w C ++?

258

Czy jest jakiś sposób na łatwe wykonanie żądania HTTP w C ++? W szczególności chcę pobrać zawartość strony (API) i sprawdzić zawartość, aby zobaczyć, czy zawiera 1 lub 0. Czy można również pobrać zawartość do łańcucha?

Sam152
źródło
1
Nie, obecnie nie ma wbudowanej obsługi ani za pośrednictwem języka, ani standardowej biblioteki do obsługi sieci. Istnieje jednak Networking TS N4370 . Zadałem również VTC to pytanie, ponieważ przyciąga ono rekomendacje bibliotek.
Co powiesz na BoostBeast?
twocrush

Odpowiedzi:

249

Miałem ten sam problem. libcurl jest naprawdę kompletny. Istnieje curlpp otoki C ++, który może Cię zainteresować, gdy poprosisz o bibliotekę C ++. neon to kolejna interesująca biblioteka C, która obsługuje również WebDAV .

curlpp wydaje się naturalny, jeśli używasz C ++. W dystrybucji źródłowej podano wiele przykładów. Aby uzyskać treść adresu URL, robisz coś takiego (wyciąg z przykładów):

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

Zobacz exampleskatalog w dystrybucji źródłowej curlpp , istnieje wiele bardziej skomplikowanych przypadków, a także prosty kompletny minimalny przy użyciu curlpp.

moje 2 centy ...

neuro
źródło
1
najnowsza wersja wydaje się być zepsuta w systemie Mac .. coś jest pomylone z config.h po podłączeniu jako biblioteka.
eugene
1
Nie mogłem skompilować powyższego. Jednak zastąpienie os << myRequest.perform();go myRequest.setOpt( new curlpp::options::WriteStream( &os ) ); myRequest.perform();dało wyniki. Pamiętaj, aby nie używać http://example.com, spowoduje to zwrócenie pustej strony. Lepsze wykorzystanie np http://www.wikipedia.org.
Zane
4
Jak budujesz curlpp w MSVS? Nie mogę go uruchomić :(
mr5
2
Nie zgadzam się z najnowszą edycją @ ryan-sam. Najwyraźniej intencją autora było napisanie „webdav”, a nie tworzenie stron internetowych, ponieważ dana biblioteka została stworzona specjalnie dla „operacji HTTP i WebDAV”.
Bostrot
2
@bostrot: Tak właśnie miałem na myśli. Cofnąłem i dodałem link, myślę, że ludzie myśleli, że napisałem webdev. Szkoda :)
neuro,
115

Kod systemu 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;
}

Oto znacznie lepsza implementacja:

#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);
}
Twórca oprogramowania
źródło
Próbowałem tego kodu w systemie Windows Vista kompilującym się z Dev-C ++ w wersji 4.9.9.2. Dałem mi kilka błędów podczas linkowania: [Błąd linkera] niezdefiniowane odniesienie do `WSAStartup @ 8 '
Expanding-Dev
4
@ Expanding-Dev Tylko MSVC (studio wizualne) rozumie „pragma comment”. Jeśli używasz czegoś innego, musisz ręcznie połączyć plik „ws2_32.lib” (jak każda inna biblioteka).
Navin
24
@JuanLuisSoldi Myślę, że naprawdę musisz być programistą Windows, aby docenić „piękno” tego kodu ...
static_rtti
Co tutaj należy odbierać (za pomocą recv)? Dostaję dużo bełkotu jako wyjścia. Ponadto, dlaczego umieściłeś to, co zrobiłeś w buforze wysyłania (np. GET / HTTP/1.1.1/... etc)? Jak dowiedzieć się, jak sformatować to, co wysyłam?
LazerSharks
43

Aktualizacja 2020: Mam nową odpowiedź, która zastępuje tę, obecnie 8-letnią, jedną: https://stackoverflow.com/a/61177330/278976

W systemie Linux wypróbowałem cpp-netlib, libcurl, curlpp, urdl, boost :: asio i zastanowiłem się nad Qt (ale odmówiłem na podstawie licencji). Wszystkie z nich były niekompletne do tego zastosowania, miały niechlujne interfejsy, miały słabą dokumentację, były nieobsługiwane lub nie obsługiwały protokołu https.

Następnie, zgodnie z sugestią https://stackoverflow.com/a/1012577/278976 , wypróbowałem POCO. Wow, szkoda, że ​​nie widziałem tego lata temu. Oto przykład tworzenia żądania HTTP GET za pomocą POCO:

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

POCO jest darmowym, otwartym oprogramowaniem (licencja doładowania). I nie, nie mam żadnych powiązań z firmą; Po prostu bardzo lubię ich interfejsy. Świetna robota chłopaki (i dziewczęta).

https://pocoproject.org/download.html

Mam nadzieję, że to pomoże komuś ... Wypróbowanie wszystkich tych bibliotek zajęło mi trzy dni.

Homer6
źródło
2
Właśnie pobrałem Poco na twoją sugestię. Wolałbym coś lekkiego, który opiera się na STL i przyspiesza, niż przepisywać dużo tego. Ponadto nie jestem fanem CppUnit, a zwłaszcza testów nienawiści uruchomionych z kompilacją i nie oczekuję, że będę musiał testować swoją bibliotekę podczas jej budowania.
CashCow
To jest trochę duże. Można jednak wyłączyć budowanie testów i próbek (lub bibliotek współdzielonych) za pomocą config (tj. --No-testy lub --no-próbki lub --no-sharedlibs). Zobacz github.com/pocoproject/poco/blob/develop/configure
Homer6
Dziękuję za to. Chcę tego mimo wszystko, ponieważ zależy mi na wykonaniu zadań, które muszę wykonać. I zauważam, że oni też tam parsują JSON, co jest dobre, ponieważ będę musiał to zrobić po wysłaniu żądania HTTP, po co mam bibliotekę.
CashCow
to było dawno temu, ale to tylko po to, aby poinformować cię, że żaden z twoich linków nie działa teraz, nawet repozytorium github wygląda na usunięte ...
Hack06
33

Opracowywane jest nowsze, mniej dojrzałe opakowanie curl o nazwie C ++ Requests . Oto proste żądanie 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;
}

Obsługuje wiele różnych czasowników HTTP i opcji zwijania. Jest więcej dokumentacja Wykorzystanie tutaj .

Oświadczenie: Jestem opiekunem tej biblioteki .

huu
źródło
10
Byłem wczoraj na twojej błyskawicznej rozmowie CppCon 2015. Dobra robota - zarówno rozmowa, jak i biblioteka. Szczególnie podoba mi się filozofia projektowania „Curl for people”.
U007D,
Witam, właśnie natknąłem się na ten post tutaj, szukając łatwiejszych żądań HTTP C ++ niż zwykły sposób. Jednak nie mam zbyt dużego doświadczenia z bibliotekami i nie wiem, jak to uwzględnić w moim projekcie wizualnym C ++. Czy jest gdzieś jakieś wytłumaczenie? Wydaje mi się, że nie jest to specyficzne dla biblioteki, ale raczej, że tak naprawdę nie wiem, co zrobić z tym, co mam przede wszystkim teraz.
Sossenbinder
2
@Sossenbinder, jeśli możesz zapoznać się z CMake, możesz za pomocą tego wygenerować pliki kompilacji Visual Studio dla tego projektu. Konfiguracja appveyor plik zawiera chropowatą przykład, jak tego dokonać.
huu
2
Wygląda ładnie, ale budowanie jest piekłem, więc twoja biblioteka jest bezużyteczna, nie mogę polegać na menedżerze pakietów (potrzebuję niezawodnego sposobu dodawania deps na zewnątrz) i potrzebuję funkcjonalnej biblioteki tak szybko, jak to możliwe ...
dev1223
tak to robisz. gdy porównasz to do 200 wierszy drugiej najbardziej uprzywilejowanej odpowiedzi .......
w.oddou
17

Oto moje minimalne opakowanie wokół cURL, aby móc pobrać stronę internetową jako ciąg znaków. Jest to przydatne na przykład do testowania jednostkowego. Jest to po prostu opakowanie RAII wokół kodu C.

Zainstaluj „libcurl” na swoim komputerze yum install libcurl libcurl-devellub jego odpowiedniku.

Przykład użycia:

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

Implementacja klasy:

#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
źródło
16

libCURL to całkiem dobra opcja dla Ciebie. W zależności od tego, co musisz zrobić, samouczek powinien powiedzieć ci, czego chcesz, szczególnie dla łatwej obsługi. Ale w zasadzie możesz to zrobić, aby zobaczyć źródło strony:

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

Wierzę, że spowoduje to wydrukowanie wyniku na standardowe wyjście. Jeśli zamiast tego chcesz to obsłużyć - co, jak zakładam, robisz - musisz ustawić CURL_WRITEFUNCTION. Wszystko to opisano w samouczku curl, do którego link znajduje się powyżej.

Wolna pamięć
źródło
16

Jak chcesz rozwiązanie C ++, możesz użyć Qt . Ma klasę QHttp, której możesz użyć.

Możesz sprawdzić dokumenty :

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

Qt ma też o wiele więcej do zaoferowania we wspólnej aplikacji C ++.

Marcelo Santos
źródło
4
Myślę, że QHttp został zastąpiony przez QNetworkAccessManager i powiązane klasy w Qt 4.6 i nowszych wersjach.
juzzlin,
3
QNetworkAccessManagerjest udokumentowany od wersji Qt 4.4; a w Qt 4.8 mówi: QHttp - This class is obsolete. It is provided to keep old source code working. We strongly advise against using it in new code.Myślę, że nadal jest dostępny, jeśli zignorujesz przestarzałe ostrzeżenia.
Jesse Chisholm,
13

Możesz sprawdzić C ++ REST SDK (nazwa kodowa „Casablanca”). http://msdn.microsoft.com/en-us/library/jj950081.aspx

Dzięki zestawowi REST SDK C ++ możesz łatwiej łączyć się z serwerami HTTP z aplikacji C ++.

Przykład użycia:

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

C ++ REST SDK to projekt firmy Microsoft do opartej na chmurze komunikacji klient-serwer w natywnym kodzie przy użyciu nowoczesnego asynchronicznego interfejsu API C ++.

tlenek miedzi
źródło
10

Z tą odpowiedzią odnoszę się do odpowiedzi Software_Developer . Po przebudowaniu kodu odkryłem, że niektóre części są przestarzałe ( gethostbyname()) lub nie zapewniają obsługi błędów (tworzenie gniazd, wysyłanie czegoś) dla operacji.

Poniższy kod systemu Windows jest testowany z programem Visual Studio 2013 i Windows 8.1 64-bit, a także Windows 7 64-bit. Będzie celować w połączenie IPv4 TCP z serwerem internetowym 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;
}

Bibliografia:

Wycofanie gethostbyname

Zwracana wartość gniazda ()

Zwracana wartość send ()

Vinz
źródło
7

C ++ nie zapewnia żadnego sposobu, aby to zrobić bezpośrednio. Zależy to całkowicie od posiadanych platform i bibliotek.

W najgorszym przypadku możesz użyć biblioteki boost :: asio do nawiązania połączenia TCP, wysłać nagłówki HTTP (RFC 2616) i bezpośrednio parsować odpowiedzi. Patrząc na twoje potrzeby aplikacji, jest to dość proste do zrobienia.

sybreon
źródło
1
Tak - przynajmniej teraz. :) stackoverflow.com/a/51959694/1599699
Andrew
@Andrew: Jeśli twoje „robi” odnosi się do sybreona, „C ++ nie zapewnia żadnego sposobu, aby to zrobić bezpośrednio”. , połączona odpowiedź jest nieprawidłowa, ponieważ pokazuje sposób na wykonanie tego przy użyciu specyfikacji systemu.
Sebastian Mach
@SebastianMach Mam na myśli, że tak. Wystarczy zaimportować bibliotekę systemową i wywołać funkcję, która wykona zadanie za Ciebie. Porównaj to ze wszystkimi innymi opcjami c ++ i albo jest to naprawdę trudne, albo przy użyciu kodu innej firmy. Uważam to za całkiem bezpośrednie.
Andrew
1
@Andrew: Jest „dostarczany przez system” tylko w systemie Windows. W innych systemach jest inaczej. I nie ma to nic wspólnego z „C ++ nie zapewnia żadnego sposobu, aby to zrobić bezpośrednio”, co tak naprawdę oznacza „Standard C ++ nie”.
Sebastian Mach
@SebastianMach Tak, ale to również subiektywne, ponieważ c ++ działa również na mikrokontrolerach tabletów telefonicznych itp. Jeśli nie każde urządzenie lub system operacyjny łatwo obsługuje jakąś funkcjonalność w c ++, czy nazywamy to nie bezpośrednio przez c ++? OP nie powiedział „c ++ standard”, po prostu powiedział c ++. Te odpowiedzi dostarczają rozwiązania dla Linuksa i Windowsa, ponieważ zwykle tego właśnie użyłbyś do czegoś takiego. Tak więc, to nie jest rozwiązanie dla Linuksa, ale tak, zapewnia je bezpośrednio główny system operacyjny.
Andrew
6

Oto kod, który będzie działał bez potrzeby korzystania z żadnej biblioteki innej firmy: Najpierw zdefiniuj bramę, użytkownika, hasło i wszelkie inne parametry, które musisz wysłać na ten konkretny serwer.

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

Oto sam kod:

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

To powinno działać w środowisku Win32 API.

Oto przykład .

Michael Haephrati
źródło
Co mam postawić na bramę? Nie ma cholernej bramy ... Win API jest taki zły.
Tomáš Zato - Przywróć Monikę
1
„Brama” to tylko ogólne słowo określające URI ( en.wikipedia.org/wiki/Uniform_Resource_Identifier ) dostarczone przez usługodawcę. To nie ma nic wspólnego z Windows.
Michael Haephrati
Ach, dzięki. Nigdy nie słyszałem tego wyrażenia, które mogłoby być użyte w adresie URL, więc trochę mnie to pomyliło. Dzięki za wyjaśnienie.
Tomáš Zato - Przywróć Monikę
Ok, przetestowałem kod i twój przykład się nie sumuje. InternetConnectzwraca null, gdy podano pełny adres URL, ale zwraca wartość inną niż null, gdy podana jest tylko nazwa domeny. Więc kiedy / gdzie mam użyć pełnego adresu URL, aby uzyskać stronę, którą chcę pobrać?
Tomáš Zato - Przywróć Monikę
Użyj InternetOpenUrl () zamiast InternetConnect (), jeśli chcesz użyć adresu URL
Al Po
4

Zaktualizowana odpowiedź na kwiecień 2020 r .:

Ostatnio odniosłem duży sukces z cpp-httplib (zarówno jako klient, jak i serwer). Jest dojrzały, a jego przybliżony, jednowątkowy RPS wynosi około 6 tys.

Co więcej, istnieje naprawdę obiecująca platforma, cpv-framework , która może uzyskać około 180 000 RPS na dwóch rdzeniach (i dobrze skaluje się z liczbą rdzeni, ponieważ jest oparta na ramie seastar , która zasila najszybsze DB na planeta, scylladb ).

Jednak szkielet cpv jest nadal stosunkowo niedojrzały; więc do większości zastosowań bardzo polecam cpp-httplib.

To zalecenie zastępuje moją poprzednią odpowiedź (8 lat temu).

Homer6
źródło
Dzięki, spróbuję może w najbliższej przyszłości;)
Hack06
Naprawdę podoba mi się podejście 1-plikowe (5K wierszy jest w porządku) cpp-httplib. Czy masz pomysł na temat jego działania?
Hack06
1
@ Hack06 Wstępny test porównawczy wynosił około 6000 żądań na sekundę (RPS).
Homer6
3

C i C ++ nie mają standardowej biblioteki dla HTTP ani nawet dla połączeń gniazd. Z biegiem lat opracowano niektóre biblioteki przenośne. Najpopularniejszym, jak powiedzieli inni, jest libcurl .

Oto lista alternatyw dla libcurl (pochodzących ze strony internetowej libcurl).

Ponadto, dla systemów Linux, to jest to prosty klient HTTP. Możesz wdrożyć własnego prostego klienta HTTP GET, ale to nie zadziała, jeśli w grę wchodzi uwierzytelnianie lub przekierowania albo jeśli potrzebujesz pracy za serwerem proxy. W takich przypadkach potrzebujesz pełnej biblioteki, takiej jak libcurl.

Dla kodu źródłowego z libcurl, to jest najbliżej, co chcesz (libCurl ma wiele przykładów ). Spójrz na główną funkcję. Zawartość html zostanie skopiowana do bufora po udanym połączeniu. Wystarczy zastąpić parseHtml własną funkcją.

kgiannakakis
źródło
3

Możesz użyć biblioteki embeddedRest . Jest to lekka biblioteka zawierająca tylko nagłówki. Dlatego łatwo jest dołączyć go do swojego projektu i nie wymaga kompilacji, ponieważ nie ma .cppw nim plików.

Poproś o przykład readme.mdz repozytorium:

#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
źródło
Nie kompiluje się w
systemie
@ uhfocuz lib jest napisany na iOS i Androida. Ale mogę pomóc w skompilowaniu go dla Win32. Nie jest to zbyt trudne
fnc12
Kocham jak światło jest dla mojego użytku, to skompilowany dobrze na moim Macu, ale na windows libs różnią się niczym dont mieć netdb.hitp więc chciałbym jakąś pomoc Tak
uhfocuz
@ uhfocuz wszystko, co musisz zrobić, to dodać warunki takie jak #ifdef _WIN32i dodać tam kod specyficzny dla systemu Windows. Spójrz tutaj - nie ma dużej różnicy między gniazdami unixowymi a gniazdami Windows. Widzę dwie główne różnice: 1) WSAStartupnajpierw zadzwoń i 2) użyj closesocketzamiastclose
fnc12
@ uhfocuz, proszę, stwórz problem w moim repozytorium - dodam obsługę win32, kiedy będę miał wystarczająco dużo czasu
fnc12
3

Protokół HTTP jest bardzo prosty, więc napisanie klienta HTTP jest bardzo proste. Tutaj jest jeden

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

Używa HTTP GET, aby pobrać plik z serwera WWW, zarówno serwer, jak i plik są parametrami wiersza poleceń. Plik zdalny jest zapisywany w lokalnej kopii.

Oświadczenie: Jestem autorem

EDYCJA: edytowany adres URL

Pedro Vicente
źródło
Podany adres URL jest nieprawidłowy.
Shravan40,
3

Pamiętaj, że nie wymaga to libcurl, Windows.h ani WinSock! Brak kompilacji bibliotek, brak konfiguracji projektu itp. Mam ten kod działający w Visual Studio 2017 c ++ na 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();

Właśnie wymyśliłem, jak to zrobić, ponieważ chciałem prostego skryptu dostępu do interfejsu API, biblioteki takie jak libcurl sprawiały mi wiele problemów (nawet gdy postępowałem zgodnie z instrukcjami ...), a WinSock jest po prostu zbyt niski i skomplikowany .

Nie jestem pewien co do całego kodu czytającego IStream (szczególnie warunek while - możesz go poprawić / poprawić), ale hej, to działa , bezproblemowo! (Ma dla mnie sens, że skoro użyłem wywołania blokującego (synchronicznego) , jest w porządku, bytesReadzawsze będzie to> 0U, dopóki strumień ( ISequentialStream ?) Nie zostanie odczytany, ale kto wie.)

Zobacz także: Monikery URL i odwołanie do protokołu asynchronicznego protokołu wtykowego

Andrzej
źródło
Został on zredagowany, ale po sporej ilości doświadczenia w c ++, będę podtrzymywał moje pierwotne twierdzenie, że jest to prawdopodobnie najłatwiejszy sposób, w jaki kiedykolwiek będziesz w stanie zrobić coś takiego w c ++ ... najmniej na razie ...)
Andrew
Właśnie przetestowałem URLOpenBlockingStream z kilkoma adresami URL z badssl.com (całkiem przydatna), a ta operacja zakończy się niepowodzeniem, jeśli certyfikat SSL jest zły. W każdym przypadku, w którym testowałem (tylko kilka), wyjściem powyższego kodu będzie pusty ciąg (brak danych strumienia). Więc to całkiem miłe.
Andrew
Zgadzam się, że jest najłatwiejszy, ale jak uzyskać kod odpowiedzi http?
Robin,
@Robin Ha! Chcesz wiadomość i kod odpowiedzi ?! Wiesz tak samo jak ja, z tym wyjątkiem, że jeśli spojrzysz na dokumentację i zajrzysz do bardziej ręcznych instrukcji Monikers URL, możesz znaleźć odpowiedź. Wydaje mi się, że pamiętam kogoś, kto publikuje kod online implementujący URLOpenBlockingStream ręcznie, co pozwoliłoby na większą konfigurację. Daj mi znać, jeśli coś wymyślisz!
Andrew
Znalazłem też to: docs.microsoft.com/en-us/windows/desktop/WinInet/… Nie mam pojęcia, czy to jest dobre.
Andrew
2

Oto niektóre (względnie) prosty kod C ++ 11, który używa libCURL do pobierania zawartości adresu URL do 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
źródło
2

Ogólnie polecam coś międzyplatformowego, jak cURL, POCO lub Qt. Oto przykład systemu 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
źródło
2

Jeśli szukasz biblioteki klienta HTTP w C ++ obsługiwanej na wielu platformach (Linux, Windows i Mac) do korzystania z usług sieciowych Restful. Możesz mieć poniższe opcje.

  1. Biblioteka sieci QT - umożliwia aplikacji wysyłanie żądań sieciowych i odbieranie odpowiedzi
  2. C ++ REST SDK - pojawiająca się biblioteka HTTP innych firm z obsługą PPL
  3. Libcurl - Jest to prawdopodobnie jeden z najczęściej używanych http lib w rodzimym świecie.
Surbhi Gupta
źródło
1

Chociaż trochę późno. Możesz preferować https://github.com/Taymindis/backcurl .

Pozwala na wykonywanie połączeń http w programowaniu mobilnym c ++. Nadaje się do tworzenia gier mobilnych

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
źródło
0

Możesz użyć ACE, aby to zrobić:

#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
źródło