Làm thế nào để bạn thực hiện một yêu cầu HTTP với C ++?


258

Có cách nào để dễ dàng thực hiện một yêu cầu HTTP với C ++ không? Cụ thể, tôi muốn tải xuống nội dung của một trang (API) và kiểm tra nội dung để xem nó có chứa 1 hay 0. Có thể tải xuống nội dung thành một chuỗi không?


1
Không, hiện tại không có hỗ trợ tích hợp nào thông qua ngôn ngữ hoặc thư viện chuẩn để kết nối mạng. Tuy nhiên, có Mạng TS N4370 . Tôi cũng VTC muốn câu hỏi này vì nó thu hút các đề xuất thư viện.

Làm thế nào về BoostBeast?
twocush

Câu trả lời:


250

Tôi đã từng gặp vấn đề tương tự. libcurl đã thực sự hoàn tất. Có một curlpp bao bọc C ++ có thể bạn quan tâm khi bạn yêu cầu một thư viện C ++. neon là một thư viện C thú vị khác cũng hỗ trợ WebDAV .

curlpp có vẻ tự nhiên nếu bạn sử dụng C ++. Có nhiều ví dụ được cung cấp trong phân phối nguồn. Để có được nội dung của một URL, bạn làm một cái gì đó tương tự (trích từ các ví dụ):

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

Xem examplesthư mục trong phân phối nguồn curlpp , có rất nhiều trường hợp phức tạp hơn, cũng như một trường hợp tối thiểu hoàn toàn đơn giản sử dụng curlpp.

2 xu của tôi ...


1
phiên bản mới nhất dường như bị hỏng dưới mac .. một cái gì đó bị rối với config.h khi được liên kết dưới dạng thư viện.
eugene

1
Vâng, tôi không thể biên dịch ở trên. Tuy nhiên, thay thế os << myRequest.perform();bằng myRequest.setOpt( new curlpp::options::WriteStream( &os ) ); myRequest.perform();kết quả đã cho. Đảm bảo không sử dụng http://example.com, điều này sẽ trả về một trang trống. Sử dụng tốt hơn, ví dụ http://www.wikipedia.org.
Zane

4
Làm thế nào để bạn xây dựng curlpp trong MSVS? Tôi không thể làm cho nó hoạt động :(
mr5

2
Tôi không đồng ý với bản chỉnh sửa mới nhất của @ ryan-sam. Rõ ràng đó là ý định của tác giả để viết "webdav" chứ không phải phát triển web vì thư viện đã cho được thực hiện rõ ràng cho "các hoạt động HTTP và WebDAV".
Bostrot

2
@bostrot: Đúng là ý tôi. Tôi hoàn nguyên và thêm một liên kết, tôi nghĩ mọi người nghĩ tôi đã viết webdev. Thật đáng tiếc :)
Neuro

115

Mã 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;
}

Đây là một thực hiện tốt hơn nhiều:

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

Đã thử mã này trên Windows Vista biên dịch với Dev-C ++ Phiên bản 4.9.9.2. Tôi đã đưa cho tôi một loạt lỗi khi liên kết: [Lỗi trình liên kết] không xác định tham chiếu đến `WSAStartup @ 8 '
Expanding-Dev

4
@ Expanding-Dev Chỉ MSVC (studio hình ảnh) hiểu "bình luận pragma". Nếu bạn sử dụng bất cứ thứ gì khác, bạn phải liên kết "ws2_32.lib" theo cách thủ công (giống như bất kỳ thư viện nào khác).
Navin

24
@JuanLuisSoldi Tôi đoán bạn thực sự cần phải là một nhà phát triển Windows để đánh giá cao "vẻ đẹp" của mã này ...
static_rtti

Những gì được cho là nhận được (sử dụng recv) ở đây? Tôi đang nhận được rất nhiều tiếng nói là đầu ra. Ngoài ra, tại sao bạn lại đặt những gì bạn đã làm vào bộ đệm gửi (ví dụ GET / HTTP/1.1.1/... etc)? Làm cách nào để tìm hiểu cách định dạng nội dung tôi gửi?
LazerSharks

43

Cập nhật 2020: Tôi có một câu trả lời mới thay thế câu hỏi này, hiện 8 tuổi, một: https://stackoverflow.com/a/61177330/278976

Trên Linux, tôi đã thử cpp-netlib, libcurl, curlpp, urdl, boost :: asio và xem xét Qt (nhưng đã từ chối dựa trên giấy phép). Tất cả những thứ này đều không đầy đủ cho việc sử dụng này, có giao diện cẩu thả, tài liệu kém, không rõ ràng hoặc không hỗ trợ https.

Sau đó, theo gợi ý của https://stackoverflow.com/a/1012577/278976 , tôi đã thử POCO. Wow, tôi ước tôi đã nhìn thấy những năm trước đây. Đây là một ví dụ về việc thực hiện yêu cầu HTTP GET với POCO:

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

POCO là miễn phí, mã nguồn mở (giấy phép tăng cường). Và không, tôi không có bất kỳ liên kết nào với công ty; Tôi thực sự thích giao diện của họ. Những người làm công việc tuyệt vời (và các cô gái).

https://pocoproject.org/doad.html

Hy vọng điều này sẽ giúp được ai đó ... tôi đã mất ba ngày để thử tất cả các thư viện này.


1
Dưới đây là một ví dụ bổ sung: github.com/pocoproject/poco/blob/develop/Net/samples/httpget/ chủ
Homer6

2
Tôi vừa tải Poco theo đề nghị của bạn. Tôi thích thứ gì đó phát sáng trên STL và tăng hơn là viết lại nhiều. Thêm vào đó, tôi không phải là fan hâm mộ của CppUnit và đặc biệt là các bài kiểm tra ghét chạy với bản dựng và không mong đợi phải kiểm tra thư viện của họ khi tôi xây dựng nó.
CashCow

Nó là một chút lớn. Tuy nhiên, bạn có thể vô hiệu hóa việc xây dựng các bài kiểm tra và mẫu (hoặc thư viện dùng chung) với cấu hình (ví dụ: --no-tests hoặc --no-samples hoặc --no-sharedlibs). Xem github.com/pocoproject/poco/blob/develop/configure
Homer6

cảm ơn vì điều đó. Dù sao thì tôi cũng muốn nó vì tôi quan tâm đến việc hoàn thành các nhiệm vụ tôi cần làm. Và tôi lưu ý rằng họ cũng có phân tích cú pháp JSON ở đó, điều này rất tốt vì tôi sẽ cần phải làm điều đó sau khi gửi yêu cầu HTTP, đó là những gì tôi đã nhận được thư viện.
CashCow

Cách đây một thời gian, nhưng điều này chỉ để cho bạn biết rằng không ai trong số các liên kết của bạn đang hoạt động, ngay cả repo github trông giống như đã bị xóa ...
Hack06

33

Có một trình bao bọc curl mới hơn, kém trưởng thành hơn đang được phát triển được gọi là Yêu cầu C ++ . Đây là một yêu cầu GET đơn giản:

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

Nó hỗ trợ nhiều loại động từ HTTP và các tùy chọn cuộn tròn. Có nhiều tài liệu sử dụng ở đây .

Tuyên bố miễn trừ trách nhiệm: Tôi là người duy trì thư viện này .


10
Tôi đã có mặt tại buổi nói chuyện chớp nhoáng CppCon 2015 của bạn ngày hôm qua. Làm tốt lắm - cả nói chuyện và thư viện. Tôi đặc biệt thích triết lý thiết kế "Curl for people".
U007D

Xin chào, tôi vừa xem bài đăng này ở đây, tìm kiếm các yêu cầu HTTP C ++ dễ dàng hơn so với cách đơn giản. Tuy nhiên, tôi không thực sự có kinh nghiệm với các thư viện và tôi thực sự không biết làm thế nào để đưa cái này vào dự án C ++ của studio hình ảnh của mình. Có lời giải thích nào ở đâu không? Tôi cảm thấy như nó không dành riêng cho thư viện, nhưng đúng hơn là tôi không thực sự biết phải làm gì với những gì tôi có trước mặt nói chung.
Sossenbinder

2
@Sossenbinder, nếu bạn có thể tự làm quen với CMake, bạn có thể tạo các tệp xây dựng Visual Studio cho dự án này bằng cách đó. Các cấu hình appveyor tập tin chứa một ví dụ thô làm thế nào để thực hiện điều này.
huu

2
Trông thật tuyệt, nhưng tòa nhà là địa ngục, vì vậy lib của bạn là vô dụng, tôi không thể dựa vào trình quản lý gói (cần cách đáng tin cậy để thêm deps bên ngoài) và cần lib chức năng càng sớm càng tốt ...
dev1223

đó là cách bạn làm điều đó khi bạn so sánh điều này với 200 dòng của câu trả lời được đánh giá cao thứ hai .......
v.oddou

17

Đây là trình bao bọc tối thiểu của tôi xung quanh cURL để có thể tìm nạp trang web dưới dạng chuỗi. Điều này rất hữu ích, ví dụ, để thử nghiệm đơn vị. Về cơ bản, nó là một trình bao bọc RAII xung quanh mã C.

Cài đặt "libcurl" trên máy của bạn yum install libcurl libcurl-develhoặc tương đương.

Ví dụ sử dụng:

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

Lớp thực hiện:

#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 là một lựa chọn khá tốt cho bạn. Tùy thuộc vào những gì bạn cần làm, hướng dẫn sẽ cho bạn biết những gì bạn muốn, đặc biệt là để xử lý dễ dàng. Nhưng, về cơ bản, bạn có thể làm điều này chỉ để xem nguồn của một trang:

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

Tôi tin rằng điều này sẽ khiến kết quả được in ra thiết bị xuất chuẩn. Nếu bạn muốn xử lý nó thay vào đó - mà theo tôi, bạn làm thế - bạn cần đặt CURL_WRITEFUNCTION. Tất cả điều đó được đề cập trong hướng dẫn curl liên kết ở trên.


16

Khi bạn muốn một giải pháp C ++, bạn có thể sử dụng Qt . Nó có một lớp QHttp mà bạn có thể sử dụng.

Bạn có thể kiểm tra tài liệu :

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

Qt cũng có nhiều hơn thế mà bạn có thể sử dụng trong một ứng dụng C ++ thông thường.


4
Tôi nghĩ rằng QHttp đã được thay thế bằng QNetworkAccessManager và các lớp liên quan trong Qt 4.6 và các phiên bản mới hơn.
juzzlin

3
QNetworkAccessManagerđã được ghi nhận từ Qt 4.4; và trong Qt 4.8 nói: QHttp - This class is obsolete. It is provided to keep old source code working. We strongly advise against using it in new code.Vì vậy, tôi đoán nó vẫn có sẵn, nếu bạn bỏ qua các cảnh báo không dùng nữa.
Jesse Chisholm

13

Bạn có thể muốn kiểm tra C ++ REST SDK (tên mã "Casablanca"). http://msdn.microsoft.com/en-us/l Library / jj950081.aspx

Với SDK C ++ REST, bạn có thể dễ dàng kết nối với máy chủ HTTP hơn từ ứng dụng C ++ của mình.

Ví dụ sử dụng:

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

SDK C ++ REST là một dự án của Microsoft dành cho giao tiếp máy chủ-máy khách dựa trên đám mây bằng mã gốc sử dụng thiết kế API C ++ không đồng bộ hiện đại.


10

Với câu trả lời này, tôi tham khảo câu trả lời từ Software_Developer . Bằng cách xây dựng lại mã, tôi thấy rằng một số phần bị phản đối ( gethostbyname()) hoặc không cung cấp xử lý lỗi (tạo ổ cắm, gửi một cái gì đó) cho một hoạt động.

Mã cửa sổ sau đây được thử nghiệm với Visual Studio 2013 và Windows 8.1 64-bit cũng như Windows 7 64-bit. Nó sẽ nhắm mục tiêu Kết nối TCP IPv4 với Máy chủ Web của 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;
}

Người giới thiệu:

Khấu hao của gethostbyname

Giá trị trả về của socket ()

Giá trị trả về của send ()


7

C ++ không cung cấp bất kỳ cách nào để làm điều đó trực tiếp. Nó hoàn toàn phụ thuộc vào nền tảng và thư viện mà bạn có.

Trong trường hợp xấu nhất, bạn có thể sử dụng thư viện boost :: asio để thiết lập kết nối TCP, gửi các tiêu đề HTTP (RFC 2616) và phân tích các phản hồi trực tiếp. Nhìn vào nhu cầu ứng dụng của bạn, điều này là đủ đơn giản để làm.


1
Nó làm - bây giờ, ít nhất. :) stackoverflow.com/a/51959694/1599699
Andrew

@Andrew: Nếu "Nó không" giải quyết vấn đề "C ++" của bạn không cung cấp bất kỳ cách nào để thực hiện trực tiếp. " , sau đó câu trả lời được liên kết là không hợp lệ vì nó cho thấy một cách để làm điều đó bằng cách sử dụng các chi tiết cụ thể của hệ thống.
Sebastian Mach

@SebastianMach Ý tôi là, mặc dù vậy. Chỉ cần nhập một thư viện do hệ thống cung cấp và gọi một chức năng, và nó thực hiện công việc cho bạn. So sánh điều đó với tất cả các tùy chọn c ++ khác và nó thực sự khó khăn hoặc sử dụng mã bên thứ 3. Tôi xem xét rằng khá trực tiếp.
Andrew

1
@Andrew: Chỉ "cung cấp hệ thống" trên Windows. Trên các hệ thống khác, nó khác. Và nó không liên quan gì đến "C ++ không cung cấp bất kỳ cách nào để thực hiện trực tiếp", điều này thực sự có nghĩa là "Tiêu chuẩn C ++ không".
Sebastian Mach

@SebastianMach Vâng, nhưng điều đó cũng chủ quan vì c ++ cũng chạy trên vi điều khiển máy tính bảng, v.v. Nếu không phải mọi thiết bị hay HĐH đều dễ dàng hỗ trợ một số chức năng trong c ++, chúng ta có gọi nó không được cung cấp trực tiếp bởi c ++ không? OP đã không nói "tiêu chuẩn c ++", anh ấy chỉ nói c ++. Những câu trả lời này đang cung cấp các giải pháp cho Linux và Windows, vì đó thường là những gì bạn sẽ sử dụng cho những thứ như thế này. Vì vậy, chắc chắn, đó không phải là một giải pháp Linux, nhưng vâng, nó được cung cấp trực tiếp bởi một hệ điều hành lớn.
Andrew

6

Đây là một số mã sẽ hoạt động mà không cần sử dụng bất kỳ thư viện bên thứ 3 nào: Trước tiên, hãy xác định cổng, người dùng, mật khẩu và bất kỳ tham số nào khác bạn cần gửi đến máy chủ cụ thể này.

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

Đây là mã chính nó:

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

Điều đó sẽ hoạt động trên môi trường API Win32.

Đây là một ví dụ .


Tôi nên đặt gì cho cổng? Không có cổng chết tiệt ... Win API rất tệ.
Tomáš Zato - Phục hồi Monica

1
"Cổng" chỉ là một từ chung cho URI ( en.wikipedia.org/wiki/Uniform_Resource_Identifier ) được cung cấp bởi nhà cung cấp dịch vụ. Điều này không có gì để làm với Windows.
Michael Haephrati

À, cảm ơn. Tôi chưa bao giờ nghe thấy biểu thức đó được sử dụng cho URL, vì vậy điều đó làm tôi bối rối. Cảm ơn đã làm rõ.
Tomáš Zato - Phục hồi Monica

Ok, tôi đã kiểm tra mã và ví dụ của bạn không thêm. InternetConnecttrả về null khi URL đầy đủ được cung cấp, nhưng trả về giá trị khác null khi chỉ cung cấp tên miền. Vậy khi nào / ở đâu tôi sử dụng URL đầy đủ để lấy trang mà tôi muốn tải xuống?
Tomáš Zato - Phục hồi Monica

Sử dụng InternetOpenUrl () thay vì InternetConnect () nếu bạn muốn sử dụng url
Al Po

4

Câu trả lời cập nhật cho tháng 4 năm 2020:

Gần đây, tôi đã có rất nhiều thành công, với cpp-omeplib (cả với tư cách là khách hàng và máy chủ). Nó trưởng thành và RPS đơn luồng, gần đúng của nó là khoảng 6k.

Trên nhiều khía cạnh khác, có một khung, cpv-framework thực sự hứa hẹn, có thể nhận được khoảng 180 nghìn RPS trên hai lõi (và sẽ mở rộng tốt với số lượng lõi vì nó dựa trên khung seastar , cung cấp năng lượng cho DB nhanh nhất hành tinh, scylladb ).

Tuy nhiên, cpv-framework vẫn còn tương đối non nớt; Vì vậy, đối với hầu hết các mục đích sử dụng, tôi đánh giá cao cpp-omeplib.

Đề nghị này thay thế câu trả lời trước đây của tôi (8 năm trước).


Cảm ơn, sẽ thử nó trong tương lai gần;)
Hack06

Tôi thực sự thích cách tiếp cận 1 tệp (5K-line là ok) của cpp-omeplib. Bạn có một ý tưởng về hiệu suất của nó?
Hack06

1
@ Hack06 Điểm chuẩn thô là khoảng 6000 yêu cầu mỗi giây (RPS).
Homer6

3

C và C ++ không có thư viện chuẩn cho HTTP hoặc thậm chí cho các kết nối ổ cắm. Trong những năm qua, một số thư viện di động đã được phát triển. Được sử dụng rộng rãi nhất, như những người khác đã nói, là libcurl .

Dưới đây là danh sách các lựa chọn thay thế cho libcurl (đến từ trang web của libcurl).

Ngoài ra, đối với Linux, đây là một máy khách HTTP đơn giản. Bạn có thể triển khai ứng dụng khách HTTP GET đơn giản của riêng mình, nhưng điều này sẽ không hoạt động nếu có xác thực hoặc chuyển hướng liên quan hoặc nếu bạn cần làm việc sau proxy. Đối với những trường hợp này, bạn cần một thư viện đầy đủ như libcurl.

Đối với mã nguồn với libcurl, đây là mã gần nhất với những gì bạn muốn (Libcurl có nhiều ví dụ ). Nhìn vào chức năng chính. Nội dung html sẽ được sao chép vào bộ đệm, sau khi kết nối thành công. Chỉ cần thay thế parseHtml với chức năng của riêng bạn.


3

Bạn có thể sử dụng embeddedRest thư viện. Đây là thư viện chỉ tiêu đề nhẹ. Vì vậy, thật dễ dàng để đưa nó vào dự án của bạn và nó không yêu cầu biên dịch vì không có .cpptệp nào trong đó.

Ví dụ yêu cầu readme.mdtừ 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;
}

Không biên dịch trên Win32: /
uhfocuz

@uhfocuz lib được viết cho iOS và Android. Nhưng tôi có thể giúp bạn biên dịch nó cho Win32. Điều đó không quá khó
fnc12

Tôi thích cách sử dụng ánh sáng của nó, nó được biên dịch tốt trên máy Mac của tôi nhưng trên Windows thì các lib khác nhau như bạn không có, netdb.hvì vậy tôi muốn có một số trợ giúp có
uhfocuz

@uhfocuz tất cả những gì bạn phải làm là thêm các điều kiện như #ifdef _WIN32và thêm vào đó mã cửa sổ cụ thể. Nhìn vào đây - không có nhiều sự khác biệt giữa ổ cắm unix và ổ cắm windows. Tôi thấy hai điểm khác biệt chính: 1) gọi WSAStartuptrước và 2) sử dụng closesocketthay vìclose
fnc12

@uhfocuz vui lòng tạo sự cố trong repo của tôi - Tôi sẽ thêm hỗ trợ win32 khi tôi có đủ thời gian
fnc12

3

Giao thức HTTP rất đơn giản, vì vậy rất đơn giản để viết một máy khách HTTP. Đây là một

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

Nó sử dụng HTTP GET để lấy tệp từ máy chủ web, cả máy chủ và tệp đều là tham số dòng lệnh. Các tập tin từ xa được lưu vào một bản sao cục bộ.

Tuyên bố miễn trừ trách nhiệm: Tôi là tác giả

EDIT: URL đã chỉnh sửa


URL đã cho không hợp lệ.
Shravan40

3

Lưu ý rằng điều này không yêu cầu libcurl, Windows.h hoặc WinSock! Không biên dịch thư viện, không cấu hình dự án, v.v. Tôi có mã này hoạt động trong Visual Studio 2017 c ++ trên 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();

Tôi chỉ tìm ra cách để làm điều này, vì tôi muốn một tập lệnh truy cập API đơn giản, các thư viện như libcurl đã gây ra cho tôi tất cả các loại vấn đề (ngay cả khi tôi làm theo chỉ dẫn ...) và WinSock chỉ ở mức độ thấp và phức tạp .

Tôi không chắc chắn về tất cả các mã đọc IStream (đặc biệt là điều kiện while - thoải mái sửa / cải thiện), nhưng này, nó hoạt động , không rắc rối! (Điều này có ý nghĩa với tôi rằng, vì tôi đã sử dụng một cuộc gọi chặn (đồng bộ) , điều này vẫn ổn, bytesReadsẽ luôn là> 0U cho đến khi luồng ( ISequentialStream ?) Được đọc xong, nhưng ai biết được.)

Xem thêm: Tham chiếu giao thức URL và biệt danh không đồng bộ


Nó đã được chỉnh sửa, nhưng sau khi có nhiều kinh nghiệm về c ++, tôi sẽ đứng trước tuyên bố ban đầu của mình rằng đây có lẽ là cách dễ nhất mà bạn từng có thể làm điều này trong c ++ ... (Tại ít nhất bây giờ ...)
Andrew

Tôi vừa kiểm tra URLOpenBlockingStream với một vài URL từ badssl.com (khá tiện dụng) và thao tác này sẽ thất bại nếu chứng chỉ SSL không tốt. Trong mọi trường hợp tôi đã kiểm tra (chỉ một vài), đầu ra của đoạn mã trên sẽ là một chuỗi trống (không có dữ liệu luồng). Điều đó thật tuyệt.
Andrew

Tôi đồng ý rằng đó là sự dễ dàng, nhưng làm thế nào bạn có thể nhận được mã phản hồi http?
Robin

@Robin Hà! Bạn muốn tin nhắn mã phản hồi?! Bạn cũng biết như tôi, ngoại trừ việc có thể nếu bạn xem tài liệu và xem các công cụ biệt danh URL thủ công hơn bạn có thể tìm thấy câu trả lời. Tôi dường như nhớ lại ai đó đăng mã trực tuyến triển khai URLOpenBlockingStream theo cách thủ công, điều này sẽ cho phép cấu hình nhiều hơn. Hãy cho tôi biết nếu bạn tìm ra bất cứ điều gì!
Andrew

Ngoài ra còn có cái này tôi vừa tìm thấy: docs.microsoft.com/en-us/windows/desktop/WinInet/. Không có ý tưởng nào nếu nó tốt.
Andrew

2

Dưới đây là một số mã C ++ 11 đơn giản (tương đối) sử dụng libCURL để tải xuống nội dung của URL vào std::vector<char>:

http_d Download.hh

# pragma once

#include <string>
#include <vector>

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

http_d 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

Nói chung, tôi muốn giới thiệu một cái gì đó đa nền tảng như cURL, POCO hoặc Qt. Tuy nhiên, đây là một ví dụ về 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

Nếu bạn đang tìm kiếm một thư viện máy khách HTTP trong C ++ được hỗ trợ trong nhiều nền tảng (Linux, Windows và Mac) để sử dụng các dịch vụ web Restful. Bạn có thể có các tùy chọn dưới đây.

  1. Thư viện mạng QT - Cho phép ứng dụng gửi yêu cầu mạng và nhận phản hồi
  2. C ++ REST SDK - Thư viện HTTP của bên thứ ba mới nổi có hỗ trợ PPL
  3. Libcurl - Nó có lẽ là một trong những http lib được sử dụng nhiều nhất trong thế giới bản địa.

1

Mặc dù hơi muộn một chút. Bạn có thể thích https://github.com/Taymindis/backcurl .

Nó cho phép bạn thực hiện cuộc gọi http trên phát triển c ++ di động. Thích hợp cho phát triển trò chơi di động

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

Bạn có thể sử dụng ACE để làm như vậy:

#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;
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.