Bagaimana Anda membuat permintaan HTTP dengan C ++?


258

Apakah ada cara untuk dengan mudah membuat permintaan HTTP dengan C ++? Secara khusus, saya ingin mengunduh konten suatu halaman (API) dan memeriksa konten untuk melihat apakah konten tersebut mengandung angka 1 atau 0. Apakah mungkin untuk mengunduh konten ke dalam sebuah string?


1
Tidak, saat ini tidak ada dukungan bawaan baik melalui bahasa atau pustaka standar untuk jaringan. Namun, ada Networking TS N4370 . Saya juga menjawab pertanyaan ini karena menarik rekomendasi perpustakaan.

Bagaimana dengan BoostBeast?
twocrush

Jawaban:


250

Saya memiliki masalah yang sama. libcurl sangat lengkap. Ada curlpp bungkus C ++ yang mungkin menarik bagi Anda saat Anda meminta pustaka C ++. neon adalah pustaka C lain yang menarik yang juga mendukung WebDAV .

curlpp tampak alami jika Anda menggunakan C ++. Ada banyak contoh yang disediakan dalam distribusi sumber. Untuk mendapatkan konten dari URL Anda melakukan sesuatu seperti itu (diekstraksi dari contoh):

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

Lihat examplesdirektori dalam distribusi sumber curlpp , ada banyak kasus yang lebih kompleks, serta yang lengkap sederhana minimal menggunakan curlpp.

2 sen saya ...


1
versi terbaru tampaknya rusak di bawah mac .. ada sesuatu yang kacau dengan config.h ketika dihubungkan sebagai perpustakaan.
eugene

1
Yah, saya tidak bisa mengkompilasi di atas. Namun, mengganti os << myRequest.perform();dengan myRequest.setOpt( new curlpp::options::WriteStream( &os ) ); myRequest.perform();memberi hasil. Pastikan untuk tidak menggunakan http://example.com, ini akan mengembalikan halaman kosong. Penggunaan yang lebih baik misalnya http://www.wikipedia.org.
Zane

4
Bagaimana Anda membangun curlpp dalam MSVS? Saya tidak bisa membuatnya bekerja :(
mr5

2
Saya tidak setuju dengan hasil edit terbaru dari @ ryan-sam. Jelas itu maksud penulis untuk menulis "webdav" dan bukan pengembangan web karena pustaka yang diberikan dibuat secara eksplisit untuk "operasi HTTP dan WebDAV".
Bostrot

2
@ Bostrot: Ya itu yang saya maksud. Saya kembali dan menambahkan tautan, saya pikir orang mengira saya menulis webdev. Sayang sekali :)
neuro

115

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

Berikut ini adalah implementasi yang jauh lebih baik:

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

Mencoba kode ini pada kompilasi Windows Vista dengan Dev-C ++ Versi 4.9.9.2. Saya memberi saya banyak kesalahan ketika menautkan: [Kesalahan tautan] tidak ditentukan referensi ke `WSAStartup @ 8 '
Expanding-Dev

4
@ Expanding-Dev Only MSVC (visual studio) memahami "komentar pragma". Jika Anda menggunakan hal lain, Anda harus menautkan "ws2_32.lib" secara manual (seperti perpustakaan lainnya).
Navin

24
@JuanLuisSoldi Saya kira Anda benar-benar harus menjadi pengembang Windows untuk menghargai "keindahan" kode ini ...
static_rtti

Apa yang seharusnya diterima (menggunakan recv) di sini? Saya mendapatkan banyak omong kosong sebagai output. Juga, mengapa Anda memasukkan apa yang Anda lakukan di buffer pengiriman (misalnya GET / HTTP/1.1.1/... etc)? Bagaimana cara mengetahui cara memformat apa yang saya kirim?
LazerSharks

43

Pembaruan 2020: Saya punya jawaban baru yang menggantikan ini, sekarang berumur 8 tahun, satu: https://stackoverflow.com/a/61177330/278976

Di Linux, saya mencoba cpp-netlib, libcurl, curlpp, urdl, boost :: asio dan mempertimbangkan Qt (tetapi menolaknya berdasarkan lisensi). Semua ini tidak lengkap untuk penggunaan ini, memiliki antarmuka yang ceroboh, memiliki dokumentasi yang buruk, tidak terawat atau tidak mendukung https.

Kemudian, atas saran https://stackoverflow.com/a/1012577/278976 , saya mencoba POCO. Wow, saya berharap saya telah melihat ini tahun lalu. Berikut adalah contoh membuat permintaan HTTP GET dengan POCO:

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

POCO gratis, open source (meningkatkan lisensi). Dan tidak, saya tidak memiliki afiliasi dengan perusahaan; Saya sangat suka antarmuka mereka. Pekerja hebat (dan cewek).

https://pocoproject.org/download.html

Semoga ini bisa membantu seseorang ... saya butuh tiga hari untuk mencoba semua perpustakaan ini.



2
Saya baru saja mengunduh Poco atas saran Anda. Saya lebih suka sesuatu yang ringan yang dibangun di atas STL dan meningkatkan daripada menulis ulang banyak dari itu. Plus, saya bukan penggemar CppUnit dan khususnya tes kebencian berjalan dengan build, dan jangan berharap harus menguji perpustakaan mereka saat saya membangunnya.
CashCow

Agak besar. Namun, Anda dapat menonaktifkan pembuatan tes dan sampel (atau pustaka bersama) dengan konfigurasi (mis. - tidak ada tes atau - tidak ada sampel atau - tidak ada yang dibagikan). Lihat github.com/pocoproject/poco/blob/develop/configure
Homer6

Terima kasih untuk itu. Saya tetap menginginkannya karena saya ingin menyelesaikan tugas yang harus saya lakukan. Dan saya perhatikan mereka memiliki JSON parsing di sana juga yang bagus karena saya akan perlu melakukan itu setelah mengirim permintaan HTTP yang adalah apa yang saya dapatkan di perpustakaan.
CashCow

beberapa saat yang lalu, tetapi ini hanya untuk memberi tahu Anda bahwa tidak ada tautan yang berfungsi sekarang, bahkan repo github sepertinya dihapus ...
Hack06

33

Ada pembungkus ikal yang lebih baru dan kurang matang yang dikembangkan disebut Permintaan C ++ . Berikut permintaan GET sederhana:

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

Ini mendukung berbagai pilihan kata kerja HTTP dan opsi ikal. Ada lebih banyak dokumentasi penggunaan di sini .

Penafian: Saya adalah pengelola perpustakaan ini .


10
Saya berada di pembicaraan kilat CppCon 2015 Anda kemarin. Bagus - pembicaraan dan perpustakaan. Saya terutama menyukai filosofi desain "Curl for people".
U007D

Halo, saya baru saja menemukan posting ini di sini, mencari lebih mudah permintaan C ++ HTTP daripada cara biasa. Namun, saya tidak terlalu berpengalaman dengan perpustakaan, dan saya tidak benar-benar tahu bagaimana memasukkan ini ke dalam proyek visual studio C ++ saya. Apakah ada penjelasan di suatu tempat? Saya merasa itu tidak spesifik untuk perpustakaan, tetapi saya tidak benar-benar tahu apa yang harus dilakukan dengan apa yang saya miliki di depan saya sekarang secara umum.
Sossenbinder

2
@Sossenbinder, jika Anda dapat membiasakan diri dengan CMake, Anda dapat membuat file Visual Studio build untuk proyek ini menggunakan itu. The konfigurasi appveyor file berisi contoh kasar tentang bagaimana untuk mencapai hal ini.
huu

2
Terlihat bagus, tetapi membangun adalah neraka, jadi lib Anda tidak berguna, saya tidak dapat mengandalkan manajer paket (perlu cara yang dapat diandalkan untuk menambahkan deps secara eksternal) dan memerlukan lib fungsional sesegera mungkin ...
dev1223

itulah cara Anda melakukannya. ketika Anda membandingkan ini dengan 200 baris jawaban kedua yang paling banyak dipilih .......
v.oddou

17

Ini adalah pembungkus minimal saya di sekitar CURL untuk dapat mengambil halaman web sebagai string. Ini berguna, misalnya, untuk pengujian unit. Ini pada dasarnya adalah pembungkus RAII di sekitar kode C.

Instal "libcurl" pada mesin Anda yum install libcurl libcurl-develatau yang setara.

Contoh penggunaan:

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

Implementasi kelas:

#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 adalah opsi yang cukup bagus untuk Anda. Bergantung pada apa yang perlu Anda lakukan, tutorial harus memberi tahu Anda apa yang Anda inginkan, khususnya untuk penanganan yang mudah. Tetapi, pada dasarnya, Anda bisa melakukan ini hanya untuk melihat sumber halaman:

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

Saya percaya ini akan menyebabkan hasil untuk dicetak menjadi stdout. Jika Anda ingin menanganinya - yang, saya asumsikan, Anda lakukan - Anda perlu mengatur CURL_WRITEFUNCTION. Semua itu tercakup dalam tutorial ikal yang ditautkan di atas.


16

Karena Anda menginginkan solusi C ++, Anda bisa menggunakan Qt . Ini memiliki kelas QHttp yang dapat Anda gunakan.

Anda dapat memeriksa dokumen :

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

Qt juga memiliki lebih banyak hal yang dapat Anda gunakan dalam aplikasi C ++ umum.


4
Saya pikir QHttp telah diganti dengan QNetworkAccessManager dan kelas terkait di Qt 4.6 dan versi yang lebih baru.
juzzlin

3
QNetworkAccessManagertelah didokumentasikan sejak Qt 4.4; dan dalam Qt 4.8 mengatakan: QHttp - This class is obsolete. It is provided to keep old source code working. We strongly advise against using it in new code.Jadi saya kira itu masih tersedia, jika Anda mengabaikan peringatan yang sudah usang.
Jesse Chisholm

13

Anda mungkin ingin memeriksa C ++ REST SDK (nama kode "Casablanca"). http://msdn.microsoft.com/en-us/library/jj950081.aspx

Dengan C ++ REST SDK, Anda dapat lebih mudah terhubung ke server HTTP dari aplikasi C ++ Anda.

Contoh penggunaan:

#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 adalah proyek Microsoft untuk komunikasi klien-server berbasis cloud dalam kode asli menggunakan desain C ++ API asinkron modern.


10

Dengan jawaban ini saya merujuk ke jawaban dari Software_Developer . Dengan membangun kembali kode saya menemukan bahwa beberapa bagian sudah usang ( gethostbyname()) atau tidak memberikan penanganan kesalahan (pembuatan soket, mengirim sesuatu) untuk operasi.

Kode windows berikut ini diuji dengan Visual Studio 2013 dan Windows 8.1 64-bit serta Windows 7 64-bit. Ini akan menargetkan Koneksi TCP IPv4 dengan Server Web dari 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;
}

Referensi:

Penghentian gethostbyname

Nilai Pengembalian soket ()

Nilai Pengembalian kirim ()


7

C ++ tidak menyediakan cara untuk melakukannya secara langsung. Ini sepenuhnya tergantung pada platform dan perpustakaan apa yang Anda miliki.

Pada kasus terburuk, Anda dapat menggunakan pustaka boost :: asio untuk membuat koneksi TCP, mengirim header HTTP (RFC 2616), dan mem-parsing respons secara langsung. Melihat kebutuhan aplikasi Anda, ini cukup sederhana untuk dilakukan.


1
Itu - sekarang, setidaknya. :) stackoverflow.com/a/51959694/1599699
Andrew

@Andrew: Jika "It do " Anda mengalamatkan sybreon's "C ++ tidak memberikan cara apa pun untuk melakukannya secara langsung." , maka jawaban yang ditautkan tidak valid karena menunjukkan cara melakukannya menggunakan spesifikasi sistem.
Sebastian Mach

@SebastianMach Maksudku, memang. Cukup impor pustaka yang disediakan sistem dan panggil fungsi, dan itu berfungsi untuk Anda. Bandingkan dengan semua opsi c ++ lainnya dan itu sangat sulit atau menggunakan kode pihak ke-3. Saya menganggap itu cukup langsung.
Andrew

1
@Andrew: Ini "disediakan sistem" hanya di Windows. Pada sistem lain, ini berbeda. Dan itu tidak ada hubungannya dengan "C ++ tidak menyediakan cara untuk melakukannya secara langsung", yang benar-benar berarti "Standar C ++ tidak".
Sebastian Mach

@SebastianMach Ya tapi itu subjektif juga karena c ++ juga berjalan di ponsel tablet mikrokontroler dll. Jika tidak setiap perangkat atau OS dengan mudah mendukung beberapa fungsi di c ++ apakah kita menyebutnya tidak secara langsung disediakan oleh c ++? OP tidak mengatakan "c ++ standar" dia hanya mengatakan c ++. Jawaban-jawaban ini memberikan solusi Linux dan Windows, karena itulah yang biasanya Anda gunakan untuk hal seperti ini. Jadi yakin, ini bukan solusi Linux, tapi ya, itu disediakan oleh OS utama secara langsung.
Andrew

6

Berikut adalah beberapa kode yang akan berfungsi tanpa perlu menggunakan perpustakaan pihak ke-3 mana pun: Pertama-tama tentukan gateway, pengguna, kata sandi Anda dan parameter lain apa pun yang perlu Anda kirim ke server khusus ini.

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

Ini kodenya sendiri:

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

Itu seharusnya bekerja pada lingkungan API Win32.

Berikut ini sebuah contoh .


Apa yang harus saya masukkan ke gateway? Tidak ada gateway sialan ... Win API sangat buruk.
Tomáš Zato - Reinstate Monica

1
"Gateway" hanyalah kata umum untuk URI ( en.wikipedia.org/wiki/Uniform_Resource_Identifier ) yang disediakan oleh penyedia layanan. Ini tidak ada hubungannya dengan Windows.
Michael Haephrati

Ah terima kasih. Saya tidak pernah mendengar ungkapan yang digunakan untuk URL, sehingga agak membingungkan saya. Terima kasih atas klarifikasi.
Tomáš Zato - Kembalikan Monica

Oke, saya menguji kodenya dan contoh Anda tidak bertambah. InternetConnectmengembalikan nol ketika URL lengkap diberikan, tetapi mengembalikan nilai bukan nol ketika hanya nama domain yang diberikan. Jadi kapan / di mana saya menggunakan URL lengkap untuk mendapatkan halaman yang ingin saya unduh?
Tomáš Zato - Reinstate Monica

Gunakan InternetOpenUrl () alih-alih InternetConnect () jika Anda ingin menggunakan url
Al Po

4

Jawaban yang diperbarui untuk April, 2020:

Saya baru saja mendapatkan banyak keberhasilan, dengan cpp-httplib (baik sebagai klien maupun server). Ini matang dan perkiraannya, RPS single-threaded sekitar 6k.

Pada lebih banyak sisi pendarahan, ada kerangka kerja yang sangat menjanjikan, cpv-framework , yang bisa mendapatkan sekitar 180k RPS pada dua core (dan akan berskala baik dengan jumlah core karena didasarkan pada kerangka seastar , yang mendukung DB tercepat di planet ini, scylladb ).

Namun, kerangka kerja cpv masih relatif belum matang; jadi, untuk sebagian besar penggunaan, saya sangat merekomendasikan cpp-httplib.

Rekomendasi ini menggantikan jawaban saya sebelumnya (8 tahun yang lalu).


Terima kasih, akan mencobanya mungkin dalam waktu dekat;)
Hack06

Saya sangat suka pendekatan 1-file (5K-lines ok) dari cpp-httplib. Apakah Anda memiliki gagasan tentang kinerjanya?
Hack06

1
@ Hack06 Tolok ukur kasarnya sekitar 6000 permintaan per detik (RPS).
Homer6

3

C dan C ++ tidak memiliki pustaka standar untuk HTTP atau bahkan untuk koneksi soket. Selama bertahun-tahun beberapa perpustakaan portabel telah dikembangkan. Yang paling banyak digunakan, seperti yang dikatakan orang lain, adalah libcurl .

Berikut adalah daftar alternatif untuk libcurl (berasal dari situs web libcurl).

Juga, untuk Linux, ini adalah klien HTTP sederhana. Anda dapat mengimplementasikan klien HTTP GET sederhana Anda sendiri, tetapi ini tidak akan berhasil jika ada otentikasi atau pengalihan yang terlibat atau jika Anda perlu bekerja di belakang proxy. Untuk kasus ini, Anda memerlukan pustaka lengkap seperti libcurl.

Untuk kode sumber dengan libcurl, ini adalah yang terdekat dengan yang Anda inginkan (Libcurl memiliki banyak contoh ). Lihatlah fungsi utamanya. Konten html akan disalin ke buffer, setelah koneksi berhasil. Cukup ganti parseHtml dengan fungsi Anda sendiri.


3

Anda dapat menggunakan perpustakaan embeddedRest . Ini adalah pustaka header-saja yang ringan. Jadi mudah untuk memasukkannya ke proyek Anda dan itu tidak memerlukan kompilasi karena tidak ada .cppfile di dalamnya.

Minta contoh dari readme.mddari repo:

#include "UrlRequest.hpp"

//...

UrlRequest request;
request.host("api.vk.com");
const auto countryId=1;
const auto count=1000;
request.uri("/method/database.getCities",{
    {"lang","ru"},
    {"country_id",countryId},
    {"count",count},
    {"need_all","1"},
});
request.addHeader("Content-Type: application/json");
auto response=std::move(request.perform());
if(response.statusCode()==200){
  cout<<"status code = "<<response.statusCode()<<", body = *"<<response.body()<<"*"<<endl;
}else{
  cout<<"status code = "<<response.statusCode()<<", description = "<<response.statusDescription()<<endl;
}

Tidak mengkompilasi pada Win32: /
uhfocuz

@uhfocuz lib ditulis untuk iOS dan Android. Tetapi saya dapat membantu Anda mengkompilasinya untuk Win32. Tidak terlalu sulit
fnc12

Saya suka betapa ringannya untuk saya gunakan, itu dikompilasi dengan baik di Mac saya tetapi pada Windows lib berbeda seperti Anda tidak punya netdb.hdll jadi saya ingin bantuan ya
uhfocuz

@uhfocuz yang harus Anda lakukan adalah menambahkan kondisi seperti #ifdef _WIN32dan menambahkan kode khusus windows. Lihat di sini - tidak ada banyak perbedaan antara soket unix dan soket windows. Saya melihat dua perbedaan utama: 1) menelepon WSAStartupdulu dan 2) menggunakan closesocketalih-alihclose
fnc12

@uhfocuz tolong buat masalah di repo saya - Saya akan menambahkan dukungan win32 begitu saya punya cukup waktu
fnc12

3

Protokol HTTP sangat sederhana, sehingga sangat mudah untuk menulis klien HTTP. Ini satu

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

Menggunakan GET HTTP untuk mengambil file dari server web, baik server dan file adalah parameter baris perintah. File jarak jauh disimpan ke salinan lokal.

Penafian: Saya penulis

EDIT: URL yang diedit


URL yang diberikan tidak valid.
Shravan40

3

Perhatikan bahwa ini tidak memerlukan libcurl, Windows.h, atau WinSock! Tidak ada kompilasi perpustakaan, tidak ada konfigurasi proyek, dll. Saya memiliki kode ini bekerja di Visual Studio 2017 c ++ pada 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();

Saya baru tahu bagaimana melakukan ini, karena saya ingin skrip akses API sederhana, perpustakaan seperti libcurl menyebabkan saya semua jenis masalah (bahkan ketika saya mengikuti petunjuk ...), dan WinSock terlalu rendah level dan rumit .

Saya tidak begitu yakin tentang semua kode membaca IStream (terutama kondisi sementara - jangan ragu untuk memperbaiki / meningkatkan), tapi hei, itu bekerja , bebas repot! (Masuk akal bagi saya bahwa, karena saya menggunakan panggilan pemblokiran (sinkron) , ini tidak apa-apa, itu bytesReadakan selalu menjadi> 0U sampai aliran ( ISequentialStream ?) Selesai dibaca, tetapi siapa yang tahu.)

Lihat juga: URL Monikers dan Referensi Protokol Pluggable Asinkron


Itu diedit, tetapi setelah cukup banyak pengalaman dalam c ++, saya akan mendukung klaim awal saya bahwa ini mungkin cara termudah yang pernah Anda lakukan untuk melakukan hal semacam ini di c ++ ... (Di Setidaknya untuk sekarang ...)
Andrew

Saya baru saja menguji URLOpenBlockingStream dengan beberapa URL dari badssl.com (sangat praktis), dan operasi ini akan gagal jika sertifikat SSL buruk. Dalam setiap kasus yang saya uji (hanya beberapa), output dari kode di atas akan menjadi string kosong (tidak ada data stream). Jadi itu cukup bagus.
Andrew

Saya setuju itu yang paling mudah, tapi bagaimana Anda mendapatkan kode respons http?
Robin

@Robin Ha! Anda menginginkan pesan dan kode responsnya ?! Anda juga tahu seperti saya, kecuali bahwa mungkin jika Anda melihat dokumentasi dan melihat lebih banyak hal manual URL Monikers Anda mungkin menemukan jawabannya. Saya sepertinya ingat seseorang memposting kode secara online mengimplementasikan URLOpenBlockingStream secara manual, yang memungkinkan konfigurasi lebih lanjut. Beri tahu saya jika Anda menemukan sesuatu!
Andrew

Ada juga ini yang baru saya temukan: docs.microsoft.com/en-us/windows/desktop/WinInet/… Tidak tahu apakah itu bagus.
Andrew

2

Berikut adalah beberapa (relatif) kode C ++ 11 sederhana yang menggunakan libCURL untuk mengunduh konten URL ke 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

Secara umum saya akan merekomendasikan sesuatu lintas platform seperti cURL, POCO, atau Qt. Namun, ini adalah contoh 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

Jika Anda mencari pustaka klien HTTP di C ++ yang didukung di banyak platform (Linux, Windows dan Mac) untuk mengonsumsi layanan web Restful. Anda dapat memiliki opsi di bawah ini.

  1. Pustaka Jaringan QT - Mengizinkan aplikasi mengirim permintaan jaringan dan menerima balasan
  2. C ++ REST SDK - Pustaka HTTP pihak ketiga yang muncul dengan dukungan PPL
  3. Libcurl - Ini mungkin salah satu http lib yang paling banyak digunakan di dunia asli.

1

Meski sedikit terlambat. Anda dapat memilih https://github.com/Taymindis/backcurl .

Hal ini memungkinkan Anda untuk melakukan panggilan http pada pengembangan c ++. Cocok untuk pengembangan game Seluler

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

Anda dapat menggunakan ACE untuk melakukannya:

#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;
}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.