Як зробити запит HTTP за допомогою C ++?


258

Чи є якийсь спосіб легко зробити запит HTTP за допомогою C ++? Зокрема, я хочу завантажити вміст сторінки (API) і перевірити вміст, щоб побачити, чи містить він 1 або 0. Чи можливо також завантажувати вміст у рядок?


1
Ні, в даний час немає вбудованої підтримки ні через мову, ні через стандартну бібліотеку для мереж. Однак є мережа TS N4370 . Я також VTC би це питання, оскільки це приваблює бібліотечні рекомендації.

Як щодо BoostBeast?
twocrush

Відповіді:


249

У мене була така ж проблема. лібкурл справді повний. Існує завитка C ++ для обгортки, яка може вас зацікавити, коли ви попросите бібліотеку C ++. neon - ще одна цікава бібліотека C, яка також підтримує WebDAV .

curlpp здається природним, якщо ви використовуєте C ++. Є багато прикладів, наданих у розподілі джерела. Щоб отримати вміст URL-адреси, ви зробите щось подібне (витягнуте із прикладів):

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

Дивіться examplesкаталог у розподілі джерела curlpp , є безліч складніших випадків, а також простий повний мінімальний з використанням curlpp.

мої 2 копійки ...


1
остання версія, здається, зламана під mac .. щось псується з config.h при посиланні на бібліотеку.
Євген

1
Ну, я не міг скласти сказане. Однак заміна os << myRequest.perform();з myRequest.setOpt( new curlpp::options::WriteStream( &os ) ); myRequest.perform();дали результатів. Переконайтеся, що не використовувати http://example.com, це поверне порожню сторінку. Краще використовувати, наприклад http://www.wikipedia.org.
Зейн

4
Як створити curlpp в MSVS? Я не можу це зробити :(
mr5

2
Я не згоден з останньою редакцією @ ryan-sam. Очевидно, автор мав намір написати "webdav", а не веб-розробку, оскільки дана бібліотека зроблена явно для "HTTP та WebDAV-операцій".
Bostrot

2
@bostrot: Так, що я мав на увазі. Я повернувся і додав посилання, я думаю, люди думали, що я написав webdev. Як шкода :)
нейро

115

Код 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;
}

Ось набагато краща реалізація:

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

Спробували цей код на Windows Vista, компілюючи версію Dev-C ++ версії 4.9.9.2. Я дав мені купу помилок під час посилання: [Linker error] undefined reference to `WSAStartup @ 8 '
Expanding-Dev

4
@ Expanding-Dev Тільки MSVC (візуальна студія) розуміє "коментар до прагми". Якщо ви використовуєте що-небудь інше, ви повинні зв’язати "ws2_32.lib" вручну (як і будь-яка інша бібліотека).
Навін

24
@JuanLuisSoldi Я думаю, вам справді потрібно бути розробником Windows, щоб оцінити "красу" цього коду ...
static_rtti

Що тут має бути отримано (використовуючи recv)? Я отримую багато гнучкості як вихід. Крім того, чому ви помістили те, що робили в буфер відправлення (наприклад GET / HTTP/1.1.1/... etc)? Як дізнатись, як відформатувати відправлене?
LazerSharks

43

Оновлення 2020 року: у мене є нова відповідь, яка замінює цю, вже 8-річну, одну: https://stackoverflow.com/a/61177330/278976

У Linux я спробував cpp-netlib, libcurl, curlpp, urdl, boost :: asio і розглядав Qt (але відмовив його від ліцензії). Усі вони або були неповними для цього використання, мали неохайні інтерфейси, мали погану документацію, не підтримувались або не підтримували https.

Потім, за пропозицією https://stackoverflow.com/a/1012577/278976 , я спробував POCO. Нічого собі, хотілося б, щоб я це бачив років тому. Ось приклад створення HTTP GET-запиту за допомогою POCO:

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

POCO безкоштовний, з відкритим кодом (ліцензія на підвищення). І ні, я не маю жодної приналежності до компанії; Мені просто дуже подобаються їхні інтерфейси. Чудові хлопці з роботи (і гелі).

https://pocoproject.org/download.html

Сподіваюся, це допоможе комусь ..., щоб спробувати всі ці бібліотеки, мені знадобилося три дні.


1
Ось додатковий приклад: github.com/pocoproject/poco/blob/develop/Net/samples/httpget/…
Homer6

2
Я щойно завантажив Поко за вашою пропозицією. Я вважаю за краще щось легке, що будується на STL та збільшує, а не переписує велику частину цього. Плюс я не прихильник CppUnit, і зокрема тестів на ненависть, які працюють із збіркою, і не сподіваюсь, що мені доведеться перевіряти їхню бібліотеку, коли я будую її.
CashCow

Він трохи великий. Однак ви можете відключити побудову тестів і зразків (або спільних бібліотек) з налаштуванням (тобто. --No-тести або --no-sample або --no-sharedlibs). Див. Github.com/pocoproject/poco/blob/develop/configure
Homer6

Дякую тобі за це. Я все одно хочу, оскільки я дбаю про виконання завдань, які мені потрібно зробити. І я зауважу, що у них там також JSON розбирає, що добре, тому що мені потрібно це зробити після відправлення HTTP-запиту, для чого я отримав бібліотеку.
CashCow

це було певний час тому, але це просто для того, щоб ви могли знати, що жодне з тих ваших посилань не працює зараз, навіть github repo виглядає видаленим ...
Hack06

33

Розробляється новіша, менш зріла обгортка для завивки під назвою C ++ Requests . Ось простий 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;
}

Він підтримує широкий спектр HTTP дієслів та варіантів curl. Там більше документації використання тут .

Відмова: Я підтримую цю бібліотеку .


10
Я вчора був на вашій блискавковій розмові CppCon 2015. Молодці - і розмова, і бібліотека. Особливо мені подобається філософія дизайну "Curl for people".
U007D

Привіт, я щойно натрапив на цю публікацію тут, шукаючи більш легких C ++ HTTP запитів, ніж звичайний спосіб. Однак я не дуже досвідчений з бібліотеками, і я не знаю, як включити це в проект візуальної студії C ++. Чи є десь пояснення? Я відчуваю, що це не характерно для бібліотеки, а швидше, що я насправді не знаю, що робити з тим, що я маю перед собою взагалі.
Sossenbinder

2
@Sossenbinder, якщо ви можете ознайомитись із CMake, ви можете створити файли для створення проекту Visual Studio для цього проекту, використовуючи це. Конфігурації appveyor файл містить грубий приклад того , як це зробити.
ху

2
Виглядає добре, але будівництво - це пекло, тому ваша лібералізація марна, я не можу покластися на менеджера пакунків (потрібен надійний спосіб, як додати deps зовні) і мені потрібна функціональна lib якнайшвидше ...
dev1223

ось так ви це робите. коли ви порівнюєте це з 200 рядками другої найбільш схваленої відповіді .......
v.oddou

17

Ось моя мінімальна обгортка навколо CURL, щоб можна було просто отримати веб-сторінку як рядок. Це корисно, наприклад, для тестування одиниць. В основному це оболонка RAII навколо коду С.

Встановіть "libcurl" на вашу машину yum install libcurl libcurl-develчи еквівалент.

Приклад використання:

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

Реалізація класу:

#include <curl/curl.h>


class CURLplusplus
{
private:
    CURL* curl;
    stringstream ss;
    long http_code;
public:
    CURLplusplus()
            : curl(curl_easy_init())
    , http_code(0)
    {

    }
    ~CURLplusplus()
    {
        if (curl) curl_easy_cleanup(curl);
    }
    std::string Get(const std::string& url)
    {
        CURLcode res;
        curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
        curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, this);

        ss.str("");
        http_code = 0;
        res = curl_easy_perform(curl);
        if (res != CURLE_OK)
        {
            throw std::runtime_error(curl_easy_strerror(res));
        }
        curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
        return ss.str();
    }
    long GetHttpCode()
    {
        return http_code;
    }
private:
    static size_t write_data(void *buffer, size_t size, size_t nmemb, void *userp)
    {
        return static_cast<CURLplusplus*>(userp)->Write(buffer,size,nmemb);
    }
    size_t Write(void *buffer, size_t size, size_t nmemb)
    {
        ss.write((const char*)buffer,size*nmemb);
        return size*nmemb;
    }
};

16

libCURL - досить хороший варіант для вас. Залежно від того, що вам потрібно зробити, підручник повинен сказати вам, що ви хочете, спеціально для легкої ручки. Але, в основному, ви могли це зробити просто щоб побачити джерело сторінки:

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

Я вважаю, що це призведе до того, що результат буде надрукований у вирівнюванні. Якщо ви хочете обробити це замість цього - що, я вважаю, ви робите - вам потрібно встановити CURL_WRITEFUNCTION. Все це висвітлено у підручнику з завитками, пов’язаним вище.


16

Оскільки вам потрібно рішення C ++, ви можете використовувати Qt . У ньому є клас QHttp, який ви можете використовувати.

Ви можете перевірити документи :

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

Qt також має багато іншого, що ви можете використовувати у звичайному додатку C ++.


4
Я думаю, що QHttp був замінений на QNetworkAccessManager і пов'язані з ним класи в Qt 4.6 і пізніших версіях.
джузлін

3
QNetworkAccessManagerзадокументовано з Qt 4.4; а в Qt 4.8 сказано: QHttp - This class is obsolete. It is provided to keep old source code working. We strongly advise against using it in new code.Отже, я думаю, він все ще доступний, якщо проігнорувати застарілі попередження.
Джессі Чизгольм

13

Ви можете перевірити C ++ REST SDK (кодова назва "Касабланка"). http://msdn.microsoft.com/en-us/library/jj950081.aspx

За допомогою програми C ++ REST SDK ви можете легше підключитися до серверів HTTP через додаток C ++.

Приклад використання:

#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 - це проект Microsoft для хмарного спілкування клієнт-сервер у вродженому коді з використанням сучасного асинхронного дизайну API C ++.


10

З цією відповіддю я посилаюся на відповідь Software_Developer . Перебудувавши код, я виявив, що деякі частини застаріли ( gethostbyname()) або не забезпечують керування помилками (створення сокетів, щось надсилання) для операції.

Наступний код Windows тестується для 64-розрядної версії Visual Studio 2013 та Windows 8.1, а також для Windows 7 64-розрядної. Він буде націлений на IP-з'єднання IPv4 TCP з веб-сервером 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;
}

Список літератури:

Деструкція gethostbyname

Повернене значення socket ()

Повернене значення відправки ()


7

C ++ не забезпечує жодного способу це зробити безпосередньо. Це повністю залежало б від платформ і бібліотек у вас.

У гіршому випадку, ви можете використовувати boost :: asio-бібліотеку для встановлення TCP-з'єднання, відправлення заголовків HTTP (RFC 2616) та безпосередньо аналізувати відповіді. З огляду на потреби вашої програми, це зробити досить просто.


1
Так - принаймні, зараз. :) stackoverflow.com/a/51959694/1599699
Андрій

@Andrew: Якщо ваш "It does" адресує sybreon, "C ++ не дає жодного способу це зробити безпосередньо". , то пов'язана відповідь не вірна, оскільки показує спосіб зробити це за допомогою специфіки системи.
Себастьян Мах

@SebastianMach Я маю на увазі, це все ж робить. Просто імпортуйте надану системою бібліотеку і зателефонуйте до функції, і вона зробить роботу за вас. Порівняйте це з усіма іншими варіантами c ++, і це або дуже важко, або з використанням стороннього коду. Я вважаю це досить прямим.
Андрій

1
@Andrew: "Система надається" лише в Windows. В інших системах це інакше. І це не має нічого спільного з "C ++ не дає ніякого способу зробити це безпосередньо", що насправді означає "стандарт C ++ не має".
Себастьян Мах

@SebastianMach Так, але це теж суб'єктивно, оскільки c ++ також працює на мікроконтролерах планшетів телефонів і т. Д. Якщо не кожен окремий пристрій чи ОС легко підтримує певну функціональність в c ++, чи не називаємо ми це безпосередньо, не надається c ++? ОП не сказав "стандарт c ++", він просто сказав c ++. Ці відповіді пропонують рішення для Linux та Windows, оскільки це типово те, що ви використовували б для подібного. Так що, це не рішення для Linux, але так, це надається безпосередньо основною ОС.
Андрій

6

Ось код, який буде працювати без необхідності використання жодної сторонньої бібліотеки: Спочатку визначте свій шлюз, користувача, пароль та будь-які інші параметри, які потрібно надіслати на цей конкретний сервер.

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

Ось сам код:

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

Це повинно працювати в середовищі API Win32.

Ось приклад .


Що слід поставити для шлюзу? Немає чортового шлюзу ... Win API так погано.
Томаш Зато -

1
"Шлюз" - це просто загальне слово для URI ( en.wikipedia.org/wiki/Uniform_Resource_Identifier ), надане постачальником послуг. Це не має нічого спільного з Windows.
Майкл Хефраті

Ах, дякую. Я ніколи не чув, щоб цей вираз використовувався для URL-адреси, так що мене це ніби не бентежило. Дякуємо за роз’яснення
Томаш Зато -

Гаразд, я перевірив код, і ваш приклад не додається. InternetConnectповертає null, коли вказана повна URL-адреса, але повертає ненулеве значення, якщо надається лише ім'я домену. Отже, коли / де я використовую повну URL-адресу, щоб отримати сторінку, яку я хочу завантажити?
Томаш Зато -

Використовуйте InternetOpenUrl () замість InternetConnect (), якщо ви хочете використовувати URL
Al Po

4

Оновлена ​​відповідь за квітень 2020 року:

Нещодавно у мене було багато успіху з cpp-httplib (як клієнтом, так і сервером). Він зрілий, його приблизний однопотоковий RPS становить близько 6 к.

Що стосується більшої частини кровотоку, існує дійсно перспективна рамка, cpv-Framework , яка може отримати близько 180 к.с. RPS на двох ядрах (і добре розширюватиметься за кількістю ядер, тому що вона базується на платформі seastar , яка забезпечує найшвидші БД на планета, сцилаб ).

Однак cpv-фреймворк все ще відносно незрілий; тому для більшості застосувань настійно рекомендую cpp-httplib.

Ця рекомендація замінює мою попередню відповідь (8 років тому).


Дякую, спробую, можливо, найближчим часом;)
Hack06,

Мені дуже подобається 1-файльний (5K-рядків нормально) підхід cpp-httplib. Чи маєте ви уявлення про його виконання?
Hack06

1
@ Hack06 Приблизний орієнтир становив близько 6000 запитів в секунду (RPS).
Гомер6

3

C і C ++ не мають стандартної бібліотеки для HTTP або навіть для з'єднань з сокетами. Протягом багатьох років були розроблені деякі портативні бібліотеки. Найбільш широко застосовується, як говорили інші, лібкурл .

Ось список альтернатив libcurl (що надходить із веб-сайту libcurl).

Також для Linux це простий клієнт HTTP. Ви можете реалізувати свій власний простий клієнт HTTP GET, але це не працюватиме, якщо є аутентифікація або переадресація або якщо вам потрібно працювати за проксі-сервером. У цих випадках вам потрібна повноцінна бібліотека на зразок libcurl.

Для вихідного коду з libcurl це найближче до того, що ви хочете (у Libcurl є багато прикладів ). Подивіться на основну функцію. Вміст html буде скопійовано в буфер після успішного з'єднання. Просто замініть parseHtml на власну функцію.


3

Ви можете використовувати вбудовану бібліотеку Rest. Це легка бібліотека, лише для заголовків. Тому легко включити його до свого проекту, і він не вимагає компіляції, оскільки .cppв ньому відсутні файли.

Запит запиту readme.mdвід репо:

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

Не компілюється на Win32: /
uhfocuz

@uhfocuz lib написаний для iOS та Android. Але я можу допомогти вам скласти його для Win32. Це не надто складно
fnc12

Мені подобається, наскільки він легкий для мого використання, він складений чудово на моєму Mac, але в Windows ліпки відрізняються, як у вас немає netdb.hі т.
Д.,

@uhfocuz все, що вам потрібно зробити, це додати такі умови, як #ifdef _WIN32і додати там специфічний код для Windows. Подивіться тут - між роз'ємами unix та розетками Windows немає великої різниці. Я бачу дві основні відмінності: 1) зателефонуйте WSAStartupспочатку та 2) використовуйте closesocketзамістьclose
fnc12

@uhfocuz, будь ласка, створіть проблему в моєму репортажі - я додам підтримку win32, коли мені вистачить часу
fnc12

3

Протокол HTTP дуже простий, тому писати HTTP-клієнт дуже просто. Ось один

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

Він використовує HTTP GET для отримання файлу з веб-сервера, і сервер, і файл - це параметри командного рядка. Віддалений файл зберігається в локальній копії.

Відмова: Я - автор

EDIT: відредагована URL-адреса


Наведена URL-адреса недійсна.
Шраван40

3

Зауважте, що для цього не потрібні libcurl, Windows.h або WinSock! Немає компіляції бібліотек, немає конфігурації проекту тощо. Цей код працює у Visual Studio 2017 c ++ у 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();

Я просто придумав, як це зробити, оскільки хотів простий скрипт доступу до API, бібліотеки на зразок libcurl викликали у мене всілякі проблеми (навіть коли я виконував вказівки ...), а WinSock - занадто низький рівень і складний .

Я не зовсім впевнений у всьому коді зчитування IStream (особливо в той час, коли умови - сміливо виправляйте / покращуйте), але ей, це працює , без проблем! (Для мене є сенс, що, оскільки я використовував блокуючий (синхронний) виклик , це добре, це bytesReadзавжди було б> 0U, поки потік ( ISequentialStream ?) Не закінчиться читання, але хто знає.)

Дивіться також: Посилання на URL-адреси та посилання на асинхронний підключений протокол


Це було відредаговано, але після неабиякого досвіду роботи з c ++, я буду відстоювати своє первісне твердження, що це, мабуть, найпростіший спосіб, коли ти зможеш робити подібні речі в c ++ ... (У принаймні поки що ...)
Андрій

Я щойно перевірив URLOpenBlockingStream з кількома URL- адресами від badssl.com (досить зручно), і ця операція не вдасться, якщо сертифікат SSL поганий. У кожному випадку, який я тестував (лише декілька), висновок вищевказаного коду буде порожнім рядком (даних потоку немає). Так що це дуже приємно.
Андрій

Я погоджуюся, що це найпростіше, але як би ви отримали код відповіді http?
Робін

@ Робін Ха! Ви хотіли повідомлення та код відповіді ?! Ви знаєте, як і я, за винятком того, що, можливо, якщо ви подивитесь на документацію і подивіться детальнішу інструкцію з URL-адрес, що описується, ви можете знайти відповідь. Здається, я згадую, що хтось розміщує код в Інтернеті, реалізуючи URLOpenBlockingStream вручну, що дозволить отримати більше конфігурації. Дайте мені знати, якщо ви щось зрозумієте!
Андрій

Я також знайшов це: docs.microsoft.com/en-us/windows/desktop/WinInet/… Поняття не маю, чи це добре.
Андрій

2

Ось (відносно) простий код C ++ 11, який використовує libCURL для завантаження вмісту URL-адреси у std::vector<char>:

http_download.hh

# pragma once

#include <string>
#include <vector>

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

http_download.cc

#include "http_download.hh"

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

using namespace std;

size_t callback(void* contents, size_t size, size_t nmemb, void* user)
{
  auto chunk = reinterpret_cast<char*>(contents);
  auto buffer = reinterpret_cast<vector<char>*>(user);

  size_t priorSize = buffer->size();
  size_t sizeIncrease = size * nmemb;

  buffer->resize(priorSize + sizeIncrease);
  std::copy(chunk, chunk + sizeIncrease, buffer->data() + priorSize);

  return sizeIncrease;
}

vector<char> download(string url, long* responseCode)
{
  vector<char> data;

  curl_global_init(CURL_GLOBAL_ALL);
  CURL* handle = curl_easy_init();
  curl_easy_setopt(handle, CURLOPT_URL, url.c_str());
  curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, callback);
  curl_easy_setopt(handle, CURLOPT_WRITEDATA, &data);
  curl_easy_setopt(handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
  CURLcode result = curl_easy_perform(handle);
  if (responseCode != nullptr)
    curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, responseCode);
  curl_easy_cleanup(handle);
  curl_global_cleanup();

  if (result != CURLE_OK)
  {
    stringstream err;
    err << "Error downloading from URL \"" << url << "\": " << curl_easy_strerror(result);
    throw runtime_error(err.str());
  }

  return move(data);
}

2

Як правило, я б рекомендував щось крос-платформне, наприклад cURL, POCO або Qt. Однак ось приклад Windows !:

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

HRESULT hr;
CComPtr<IXMLHTTPRequest> request;

hr = request.CoCreateInstance(CLSID_XMLHTTP60);
hr = request->open(
    _bstr_t("GET"),
    _bstr_t("https://www.google.com/images/srpr/logo11w.png"),
    _variant_t(VARIANT_FALSE),
    _variant_t(),
    _variant_t());
hr = request->send(_variant_t());

// get status - 200 if succuss
long status;
hr = request->get_status(&status);

// load image data (if url points to an image)
VARIANT responseVariant;
hr = request->get_responseStream(&responseVariant);
IStream* stream = (IStream*)responseVariant.punkVal;
CImage *image = new CImage();
image->Load(stream);
stream->Release();

2

Якщо ви шукаєте бібліотеку клієнтів HTTP в C ++, яка підтримується на декількох платформах (Linux, Windows і Mac) для використання веб-сервісів Restful. Ви можете мати нижче варіанти.

  1. QT Network Library - дозволяє програмі надсилати мережеві запити та отримувати відповіді
  2. C ++ REST SDK - стороння бібліотека HTTP з підтримкою PPL
  3. Libcurl - це, мабуть, одна з найбільш використовуваних http lib у рідному світі.

1

Хоча трохи пізно. Ви можете віддати перевагу https://github.com/Taymindis/backcurl .

Це дозволяє робити http-дзвінок на мобільних розробках c ++. Підходить для мобільних ігор

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

bcl::execute<std::string>([&](bcl::Request *req) {
    bcl::setOpts(req, CURLOPT_URL , "http://www.google.com",
             CURLOPT_FOLLOWLOCATION, 1L,
             CURLOPT_WRITEFUNCTION, &bcl::writeContentCallback,
             CURLOPT_WRITEDATA, req->dataPtr,
             CURLOPT_USERAGENT, "libcurl-agent/1.0",
             CURLOPT_RANGE, "0-200000"
            );
}, [&](bcl::Response * resp) {
    std::string ret =  std::string(resp->getBody<std::string>()->c_str());
    printf("Sync === %s\n", ret.c_str());
});


bcl::cleanUp(); // clean up when no more using

0

Для цього можна використовувати ACE:

#include "ace/SOCK_Connector.h"

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

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

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

    return 0;
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.