Как вы делаете HTTP-запрос с C ++?


258

Есть ли способ легко сделать HTTP-запрос с C ++? В частности, я хочу загрузить содержимое страницы (API) и проверить содержимое, чтобы увидеть, содержит ли оно 1 или 0. Можно ли также загрузить содержимое в строку?


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

Как насчет BoostBeast?
Twocrush

Ответы:


250

У меня такая же проблема. libcurl действительно завершен. Существует обертка C ++ curlpp, которая может вас заинтересовать, когда вы запрашиваете библиотеку 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».
Бострот

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. Я дал мне несколько ошибок при компоновке: [ошибка компоновщика] неопределенная ссылка на `WSAStartup @ 8 '
Expanding-Dev

4
@ Expanding-Dev Only MSVC (visual studio) понимает «прагматический комментарий». Если вы используете что-то еще, вы должны связать «ws2_32.lib» вручную (как любая другая библиотека).
Навин

25
@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 является бесплатным, с открытым исходным кодом (лицензия Boost). И нет, я не имею никакого отношения к компании; Мне просто очень нравятся их интерфейсы. Отличная работа, ребята (и девочки).

https://pocoproject.org/download.html

Надеюсь, это кому-нибудь поможет ... Мне понадобилось три дня, чтобы опробовать все эти библиотеки.



2
Я только что скачал Poco по твоему предложению. Я бы предпочел что-то более легкое, основанное на STL и улучшающее, а не переписывающее большую часть этого. Кроме того, я не фанат CppUnit и, в частности, тестов на ненависть, выполняемых со сборкой, и не ожидаю, что мне придется тестировать их библиотеку во время ее сборки.
CashCow

Это немного большой. Однако вы можете отключить сборку тестов и примеров (или разделяемых библиотек) с помощью configure (т. Е. --No-tests или --no-samples или --no-sharedlibs). См github.com/pocoproject/poco/blob/develop/configure
Homer6

Спасибо вам за это. В любом случае, я хочу этого, потому что я забочусь о том, чтобы выполнить задачи, которые мне нужно сделать. И я отмечаю, что там тоже есть разбор JSON, и это хорошо, так как мне нужно будет это сделать после отправки HTTP-запроса, для которого я и получил библиотеку.
CashCow

это было некоторое время назад, но это только для того, чтобы вы знали, что ни одна из ваших ссылок не работает, даже репозиторий github выглядит как удаленный ...
Hack06

33

В настоящее время разрабатывается более новая, менее зрелая оболочка curl, которая называется 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, чем простой способ. Тем не менее, я не очень разбираюсь в библиотеках и не знаю, как включить это в свой проект Visual Studio C ++. Есть ли где-нибудь объяснение? Я чувствую, что это не определенно для библиотеки, а скорее, что я действительно не знаю, что делать с тем, что у меня сейчас передо мной вообще.
Sossenbinder

2
@Sossenbinder, если вы можете ознакомиться с CMake, вы можете создать файлы сборки Visual Studio для этого проекта, используя это. Конфигурации appveyor файл содержит грубый пример того , как это сделать.
Ху

2
Выглядит неплохо, но сборка адская, так что ваша библиотека бесполезна, я не могу положиться на менеджер пакетов (нужен надежный способ, как добавить deps извне) и
мне

вот как ты это делаешь. когда вы сравните это с 200 строками второго ответа с наибольшим количеством голосов ........
v.oddou

17

Вот моя минимальная оболочка вокруг cURL, чтобы можно было просто получить веб-страницу в виде строки. Это полезно, например, для модульного тестирования. Это в основном оболочка RAII вокруг кода C.

Установите «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 и более поздних версиях.
Juzzlin

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 для облачной связи клиент-сервер в собственном коде с использованием современного асинхронного C ++ API.


10

С этим ответом я ссылаюсь на ответ от Software_Developer . Перестраивая код, я обнаружил, что некоторые части устарели ( gethostbyname()) или не обеспечивают обработку ошибок (создание сокетов, отправку чего-либо) для операции.

Следующий код Windows протестирован с 64-битной Visual Studio 2013 и Windows 8.1, а также с 64-битной Windows 7. Он будет нацелен на TCP-соединение IPv4 с веб-сервером 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

Возвращаемое значение сокета ()

Возвращаемое значение send ()


7

C ++ не предоставляет никакого способа сделать это напрямую. Это будет полностью зависеть от того, какие платформы и библиотеки у вас есть.

В худшем случае вы можете использовать библиотеку boost :: asio для установления TCP-соединения, отправки заголовков HTTP (RFC 2616) и непосредственного анализа ответов. Глядя на потребности вашего приложения, это достаточно просто сделать.


1
Это так - сейчас, по крайней мере. :) stackoverflow.com/a/51959694/1599699
Эндрю

@Andrew: Если ваше «Это» обращается к Sybreon, «C ++ не предоставляет никакого способа сделать это напрямую». , тогда связанный ответ недействителен, поскольку он показывает способ сделать это, используя специфику системы.
Себастьян Мах

@SebastianMach Я имею в виду, это так, хотя. Просто импортируйте системную библиотеку и вызовите функцию, и она сделает всю работу за вас. Сравните это со всеми остальными опциями c ++, и это либо очень сложно, либо использует сторонний код. Я считаю это довольно прямым.
Андрей

1
@Andrew: «Предоставляется системой» только в Windows. В других системах все по-другому. И это не имеет ничего общего с «C ++ не предоставляет никакого способа сделать это напрямую», что в действительности означает «стандарт C ++ не делает».
Себастьян Мах

@SebastianMach Да, но это тоже субъективно, потому что 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);

Это должно работать в среде Win32 API.

Вот пример .


Что я должен поставить для шлюза? Там нет ни черта шлюза ... Win API это так плохо.
Томаш Зато - Восстановить Монику

1
«Шлюз» - это просто общее слово для URI ( en.wikipedia.org/wiki/Uniform_Resource_Identifier ), предоставляемое поставщиком услуг. Это не имеет ничего общего с Windows.
Майкл Хефрати

Ах, спасибо. Я никогда не слышал, чтобы это выражение использовалось для URL, так что это меня немного смутило. Спасибо за разъяснение.
Томаш Зато - Восстановить Монику

Хорошо, я проверил код, и ваш пример не складывается. InternetConnectвозвращает нулевое значение, когда указан полный URL-адрес, но возвращает ненулевое значение, если указано только доменное имя. Так, когда / где я могу использовать полный URL, чтобы получить страницу, которую я хочу загрузить?
Томаш Зато - Восстановить Монику

Используйте InternetOpenUrl () вместо InternetConnect (), если вы хотите использовать URL
Al Po

4

Обновленный ответ за апрель 2020 года:

Недавно у меня был большой успех с cpp-httplib (как клиентом, так и сервером). Это зрелый и его приблизительный, однопоточный RPS составляет около 6 КБ.

Более того, есть действительно многообещающая инфраструктура, cpv-framework , которая может получить около 180 тыс. RPS на двух ядрах (и будет хорошо масштабироваться с количеством ядер, потому что она основана на инфраструктуре Seastar , которая обеспечивает работу самых быстрых БД на планета, scylladb ).

Однако, cpv-framework все еще относительно незрелый; поэтому для большинства случаев я настоятельно рекомендую cpp-httplib.

Эта рекомендация заменяет мой предыдущий ответ (8 лет назад).


Спасибо, попробую, может быть, в ближайшее время;)
Hack06

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

1
@ Hack06 Примерный тест составлял около 6000 запросов в секунду (RPS).
Homer6

3

C и C ++ не имеют стандартной библиотеки для HTTP или даже для сокетных соединений. За эти годы были разработаны некоторые портативные библиотеки. Как уже говорили другие, наиболее широко используемым является libcurl .

Вот список альтернатив libcurl (с веб-сайта libcurl).

Также для Linux это простой HTTP-клиент. Вы можете реализовать свой собственный простой HTTP-клиент GET, но это не сработает, если задействованы аутентификация или перенаправления, или если вам нужно работать за прокси-сервером. Для этих случаев вам нужна полноценная библиотека, такая как libcurl.

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


3

Вы можете использовать встроенную библиотеку. Это легкая библиотека только для заголовков. Поэтому его легко включить в ваш проект, и он не требует компиляции, потому что в нем нет .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 библиотека написана для 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 для извлечения файла с веб-сервера, и сервер, и файл являются параметрами командной строки. Удаленный файл сохраняется в локальной копии.

Отказ от ответственности: я автор

РЕДАКТИРОВАТЬ: отредактированный URL


Данный URL недействителен.
Shravan40

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 (особенно в условиях while - не стесняйтесь исправлять / улучшать), но эй, это работает , без проблем! (Для меня имеет смысл, что, поскольку я использовал блокирующий (синхронный) вызов , это нормально, это bytesReadвсегда будет> 0U, пока поток ( ISequentialStream ?) Не закончит чтение, но кто знает.)

См. Также: URL Monikers и Asynchronous Pluggable Protocol Справочник


Он был отредактирован, но после достаточного опыта работы с c ++ я собираюсь поддержать мое первоначальное утверждение, что это, вероятно, самый простой способ, которым вы когда-либо сможете делать такие вещи в c ++ ... (В хотя бы пока ...)
Андрей

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

Я согласен, что это проще всего, но как бы вы получили код ответа http?
Робин

@ Робин Ха! Вы хотели сообщение и код ответа ?! Вы знаете так же хорошо, как и я, за исключением того, что, возможно, если вы посмотрите документацию и загляните в более подробные руководства по URL Monikers, вы можете найти ответ. Кажется, я вспоминаю, как кто-то выкладывал код в Интернете, реализуя 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 библиотек в родном мире.

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.