有什么方法可以使用C ++轻松发出HTTP请求吗?具体来说,我想下载页面(API)的内容并检查内容是否包含1或0。是否还可以将内容下载到字符串中?

评论

不,目前没有通过语言或标准库进行联网的内置支持。但是,有一个网络TS N4370。我也VTC提出了这个问题,因为它吸引了图书馆的建议。

BoostBeast怎么样?

@ user3920237幸运的是,大多数人对VTC都没有破坏性的思考。这些是供人们使用的,请成为图书馆。

#1 楼

我有同样的问题。 libcurl确实很完整。当您要求使用C ++库时,可能会有一个C ++包装程序curlpp引起您的兴趣。 neon是另一个有趣的C库,它也支持WebDAV。
如果使用C ++,curlpp看起来很自然。源分发中提供了许多示例。
要获取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();

请参见curlpp源分发中的examples目录,有很多更复杂的案例,还有一个使用curlpp的简单完整的案例。
我的2美分...

评论


在Mac下最新版本似乎已损坏。以库链接时config.h混乱。

– Eugene
2011年4月8日,0:59

好吧,我无法编译以上内容。但是,替换os << myRequest.perform();使用myRequest.setOpt(new curlpp :: options :: WriteStream(&os)); myRequest.perform();给出了结果。确保不使用http://example.com,这将返回一个空白页。更好地使用例如http://www.wikipedia.org。

–赞恩
2012年11月4日14:22



如何在MSVS中构建curlpp?我不能让它工作:(

–mr5
16-11-15在6:43

我不同意@ ryan-sam的最新编辑。显然,作者的意图是编写“ webdav”而不是进行Web开发,因为给定的库是为“ HTTP和WebDAV操作”明确创建的。

–ostro
18/12/5在11:20

@bostrot:是的,我的意思是。我回复并添加了一个链接,我认为人们以为我写了webdev。太遗憾了 :)

–神经
18/12/5在13:43

#2 楼

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


评论


我无法使curlpp正常工作,所以我使用了您的代码,谢谢

– Shereef Marzouk
2012年11月25日在11:04

在使用Dev-C ++版本4.9.9.2。编译的Windows Vista上尝试了此代码。链接时出现了很多错误:[链接器错误]对'WSAStartup @ 8'的未定义引用

– Expanding-Dev
2014年4月2日在20:15

@ Expanding-Dev只有MSVC(Visual Studio)了解“编译注释”。如果使用其他任何工具,则必须手动链接“ ws2_32.lib”(就像其他任何库一样)。

– Navin
14年5月15日在18:37

在这里应该收到什么(使用recv)?我得到了很多乱七八糟的输出。此外,为什么还要将发送的内容放入发送缓冲区(例如GET / HTTP / 1.1.1 / ...等)?我如何找到如何格式化发送的内容?

–LazerSharks
2014年9月8日在0:27



#3 楼

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。哇,我希望我几年前见过。以下是使用POCO发出HTTP GET请求的示例:

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

POCO是免费的,开源的(提升许可) )。不,我与该公司没有任何隶属关系;我只是真的很喜欢他们的界面。很棒的人(和gal)。

https://pocoproject.org/download.html

希望这对某人有帮助...我花了三天的时间尝试所有这些库中。

评论


这是另一个示例:github.com/pocoproject/poco/blob/develop/Net/samples/httpget/…

– Homer6
2014年1月10日21:32

我只是根据您的建议下载了Poco。我更喜欢在STL上构建和增强功能的轻量级内容,而不是重写其中的大部分内容。另外,我不是CppUnit的爱好者,尤其是不喜欢在构建过程中运行的仇恨测试,并且不要指望在构建它时必须对其库进行测试。

– CashCow
2014年2月25日在21:30



它有点大。但是,您可以使用configure(即--no-tests或--no-samples或--no-sharedlibs)禁用构建测试和示例(或共享库)。见github.com/pocoproject/poco/blob/develop/configure

– Homer6
2014年2月25日在22:34



谢谢你无论如何,我都想这样做,因为我关心着完成需要做的任务。而且我注意到他们也有JSON解析,这很好,因为在发送HTTP请求(这是我得到的库)之后,我将需要这样做。

– CashCow
2014年2月26日在13:37

@ Hack06感谢您的评论。我提供了一个新的答案,现在取代了这个答案:stackoverflow.com/a/61177330/278976

– Homer6
4月12日19:58

#4 楼

有一种较新的,不太成熟的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选项。这里有更多用法文档。

免责声明:我是该库的维护者。

评论


昨天我在您的CppCon 2015闪电演讲中。做得好-演讲和图书馆都很好。我特别喜欢“为人卷曲”的设计理念。

– U007D
2015年9月23日下午13:59



您好,我只是在这里碰到这篇文章,寻找比普通方式更简单的C ++ HTTP请求。但是,我对库没有真正的经验,而且我真的不知道如何将其包含在我的Visual Studio C ++项目中。那里有什么解释吗?我觉得它不是特定于库的,而是我通常并不真正知道该如何处理现在我面前的东西。

– Sossenbinder
16年1月17日在21:18

@Sossenbinder,如果您熟悉CMake,则可以使用该文件为该项目生成Visual Studio构建文件。 Appveyor配置文件包含有关如何完成此操作的粗略示例。

–huu
16-2-4在3:41

看起来不错,但是构建很麻烦,所以您的库没用,我不能依靠包管理器(需要可靠的方法在外部添加deps的方法),并且需要尽快使用功能性库...

–dev1223
16 Sep 15'在12:43

那就是你的做法。当您将此与第二个最受好评的答案的200行进行比较时……。

–v.oddou
18年4月11日15:57

#5 楼

这是我对cURL的最小包装,仅能以字符串形式获取网页。例如,这对于单元测试很有用。它基本上是围绕C代码的RAII包装器。

在计算机上安装“ libcurl” yum install libcurl libcurl-devel或等效版本。

使用示例:

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


类实现:

#include <curl/curl.h>


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

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

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


#6 楼

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。所有这些都在上面链接的curl教程中介绍。

#7 楼

如果需要C ++解决方案,可以使用Qt。它有一个可以使用的QHttp类。

您可以检查文档:

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


Qt还为您提供了更多功能可以在常见的C ++应用中使用。

评论


我认为QHttp 4.6和更高版本已将QHttp替换为QNetworkAccessManager和相关类。

– juzzlin
2015年5月5日在21:31



从Qt 4.4开始已经记录了QNetworkAccessManager。在Qt 4.8中说:QHttp-此类已过时。提供它是为了使旧的源代码正常工作。我们强烈建议不要在新代码中使用它。因此,如果您忽略不推荐使用的警告,我想它仍然可用。

–杰西·奇斯霍尔姆(Jesse Chisholm)
2015年8月5日在23:55



#8 楼

您可能要检查C ++ REST SDK(代号“ Casablanca”)。 http://msdn.microsoft.com/zh-cn/library/jj950081.aspx

使用C ++ REST SDK,您可以更轻松地从C ++应用连接到HTTP服务器。

用法示例:

#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设计以本机代码进行基于云的客户端-服务器通信。 。

评论


这个有明确的指示,我刚试过

–像素
8月17日19:58

#9 楼

关于这个答案,我指的是Software_Developer的答案。通过重建代码,我发现某些部件已被弃用(gethostbyname())或不提供操作错误处理(套接字创建,发送内容)。

以下Windows代码已通过Visual Studio 2013测试和Windows 8.1 64位以及Windows 7 64位。它将以与www.google.com的Web服务器的IPv4 TCP连接为目标。

#include <winsock2.h>
#include <WS2tcpip.h>
#include <windows.h>
#include <iostream>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
    int main (){
    // Initialize Dependencies to the Windows Socket.
    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0) {
        cout << "WSAStartup failed.\n";
        system("pause");
        return -1;
    }

    // We first prepare some "hints" for the "getaddrinfo" function
    // to tell it, that we are looking for a IPv4 TCP Connection.
    struct addrinfo hints;
    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family = AF_INET;          // We are targeting IPv4
    hints.ai_protocol = IPPROTO_TCP;    // We are targeting TCP
    hints.ai_socktype = SOCK_STREAM;    // We are targeting TCP so its SOCK_STREAM

    // Aquiring of the IPv4 address of a host using the newer
    // "getaddrinfo" function which outdated "gethostbyname".
    // It will search for IPv4 addresses using the TCP-Protocol.
    struct addrinfo* targetAdressInfo = NULL;
    DWORD getAddrRes = getaddrinfo("www.google.com", NULL, &hints, &targetAdressInfo);
    if (getAddrRes != 0 || targetAdressInfo == NULL)
    {
        cout << "Could not resolve the Host Name" << endl;
        system("pause");
        WSACleanup();
        return -1;
    }

    // Create the Socket Address Informations, using IPv4
    // We dont have to take care of sin_zero, it is only used to extend the length of SOCKADDR_IN to the size of SOCKADDR
    SOCKADDR_IN sockAddr;
    sockAddr.sin_addr = ((struct sockaddr_in*) targetAdressInfo->ai_addr)->sin_addr;    // The IPv4 Address from the Address Resolution Result
    sockAddr.sin_family = AF_INET;  // IPv4
    sockAddr.sin_port = htons(80);  // HTTP Port: 80

    // We have to free the Address-Information from getaddrinfo again
    freeaddrinfo(targetAdressInfo);

    // Creation of a socket for the communication with the Web Server,
    // using IPv4 and the TCP-Protocol
    SOCKET webSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (webSocket == INVALID_SOCKET)
    {
        cout << "Creation of the Socket Failed" << endl;
        system("pause");
        WSACleanup();
        return -1;
    }

    // Establishing a connection to the web Socket
    cout << "Connecting...\n";
    if(connect(webSocket, (SOCKADDR*)&sockAddr, sizeof(sockAddr)) != 0)
    {
        cout << "Could not connect";
        system("pause");
        closesocket(webSocket);
        WSACleanup();
        return -1;
    }
    cout << "Connected.\n";

    // Sending a HTTP-GET-Request to the Web Server
    const char* httpRequest = "GET / HTTP/1.1\r\nHost: www.google.com\r\nConnection: close\r\n\r\n";
    int sentBytes = send(webSocket, httpRequest, strlen(httpRequest),0);
    if (sentBytes < strlen(httpRequest) || sentBytes == SOCKET_ERROR)
    {
        cout << "Could not send the request to the Server" << endl;
        system("pause");
        closesocket(webSocket);
        WSACleanup();
        return -1;
    }

    // Receiving and Displaying an answer from the Web Server
    char buffer[10000];
    ZeroMemory(buffer, sizeof(buffer));
    int dataLen;
    while ((dataLen = recv(webSocket, buffer, sizeof(buffer), 0) > 0))
    {
        int i = 0;
        while (buffer[i] >= 32 || buffer[i] == '\n' || buffer[i] == '\r') {
            cout << buffer[i];
            i += 1;
        }
    }

    // Cleaning up Windows Socket Dependencies
    closesocket(webSocket);
    WSACleanup();

    system("pause");
    return 0;
}


参考:

gethostbyname的弃用

socket()的返回值

send()的返回值

#10 楼

2020年4月的最新答案:

最近,我使用cpp-httplib(同时作为客户端和服务器)取得了很多成功。它已经成熟,大约单线程RPS约为6k。

在最前沿的情况下,有一个非常有前途的框架cpv-framework,它可以在两个内核上获得约180k RPS。它将基于内核数量很好地扩展,因为它基于seastar框架,该框架为地球上最快的DB提供支持(scylladb)。

但是,cpv框架仍然相对不成熟。因此,对于大多数用途,我强烈建议使用cpp-httplib。

该建议代替了我之前的回答(8年前)。

评论


谢谢,也许会在不久的将来尝试一下;)

–Hack06
4月12日20:42

我真的很喜欢cpp-httplib的1-file(5K行是可以的)方法。您对它的性能有想法吗?

–Hack06
5月3日15:44

@ Hack06粗略的基准测试大约是每秒6000个请求(RPS)。

– Homer6
5月10日18:40

#11 楼

C ++没有提供直接执行此操作的任何方法。这完全取决于您拥有的平台和库。在最坏的情况下,您可以使用boost :: asio库建立TCP连接,发送HTTP标头(RFC 2616)和直接解析响应。查看您的应用程序需求,这很简单。

评论


它-至少现在如此。 :) stackoverflow.com/a/51959694/1599699

–安德鲁(Andrew)
18年8月22日在4:08

@Andrew:如果您的“它确实”解决了sybreon的“ C ++没有提供任何直接方法来执行此操作。”,则链接的答案无效,因为它显示了使用系统特定方法来执行此操作的方法。

–塞巴斯蒂安·马赫
19-09-9 13:23

@SebastianMach我的意思是,但是可以。只需导入系统提供的库并调用函数,即可为您完成工作。将其与所有其他c ++选项进行比较,这要么真的很困难,要么使用第三方代码。我认为那很直接。

–安德鲁(Andrew)
19年9月9日在14:26

@Andrew:仅在Windows上是“系统提供的”。在其他系统上,情况有所不同。而且它与“ C ++没有提供直接实现它的任何方式”没有任何关系,这实际上意味着“ C ++标准没有”。

–塞巴斯蒂安·马赫
19/09/10'7:49

@SebastianMach是的,但这也是主观的,因为c ++也可以在手机平板电脑微控制器等上运行。轻松支持c ++中的某些功能,我们称它不是c ++直接提供的吗? O.P.没有说“ c ++标准”,他只是说了c ++。这些答案提供了Linux和Windows解决方案,因为通常这就是您将要使用的解决方案。可以肯定的是,这不是Linux解决方案,但是是的,它是由主要的操作系统提供的。直。

–安德鲁(Andrew)
19-09-10的16:45

#12 楼

以下是一些无需使用任何第三方库即可使用的代码:
首先定义您的网关,用户,密码和其他需要发送到此特定服务器的参数。

#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太糟糕了。

–TomášZato-恢复莫妮卡
19年5月24日在9:21

“网关”只是服务提供商提供的URI(en.wikipedia.org/wiki/Uniform_Resource_Identifier)的通用词。这与Windows无关。

– Michael Haephrati
19年5月24日,11:12

嗯谢谢我从未听说过该表达式可用于URL,所以让我感到困惑。感谢您的澄清。

–TomášZato-恢复莫妮卡
19年5月24日在12:12

好的,我测试了代码,您的示例没有累加。当给出完整的URL时,InternetConnect返回null,但是当仅提供域名时,InternetConnect返回非null值。因此,何时/何地使用完整URL来获取要下载的页面?

–TomášZato-恢复莫妮卡
19年5月24日在12:52

如果要使用url,请使用InternetOpenUrl()而不是InternetConnect()

– Al Po
19年8月9日在20:03

#13 楼

C和C ++没有用于HTTP甚至套接字连接的标准库。多年来,已经开发了一些便携式库。正如其他人所说,使用最广泛的是libcurl。

这里是libcurl的替代列表(来自libcurl的网站)。

对于Linux ,这是一个简单的HTTP客户端。您可以实现自己的简单HTTP GET客户端,但是如果涉及身份验证或重定向,或者需要在代理后面工作,则此方法将无效。对于这些情况,您需要一个成熟的库,例如libcurl。对于带有libcurl的源代码,这是最接近您想要的库(Libcurl有很多示例)。看一下主要功能。成功连接后,html内容将被复制到缓冲区。只需用您自己的函数替换parseHtml。

#14 楼

请注意,这不需要libcurl,Windows.h或WinSock!没有库的编译,没有项目配置等。我有这段代码可以在Windows 10的Visual Studio 2017 c ++中运行:

#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条件-随时进行纠正/改进),但是,它确实有效,没有麻烦! (对我来说,因为我使用了阻塞(同步)调用,所以这很正常,直到流(ISequentialStream?)读完为止,bytesRead始终> 0U,但是谁知道。)

另请参阅:URL Monikers和异步可插拔协议参考

评论


它已被删除,但是经过大量的c ++经验之后,我将坚持我最初的主张,即这可能是您将能够在c ++中执行这种操作的最简单方法...(在至少现在...)

–安德鲁(Andrew)
18年8月23日在22:37



我刚刚使用badssl.com提供的一些URL测试了URLOpenBlockingStream(非常方便),如果SSL证书不正确,此操作将失败。在我测试的每种情况下(仅少数情况),上述代码的输出将为空字符串(无流数据)。所以这很好。

–安德鲁(Andrew)
18年8月24日在1:04



我同意这是最简单的方法,但是您将如何获得http响应代码?

–罗宾
18年8月28日在11:37

@罗宾哈!您需要消息和响应代码吗?您和我一样了解,除了也许您会阅读文档并浏览更手册的URL Monikers手册时,可能会找到答案。我似乎想起有人在网上手动发布实现URLOpenBlockingStream的代码,这将允许更多配置。如果您有任何想法,请告诉我!

–安德鲁(Andrew)
18年8月28日在17:33

我刚刚发现的还有这个:docs.microsoft.com/en-us/windows/desktop/WinInet/…不知道它是否有用。

–安德鲁(Andrew)
18年8月28日在17:34

#15 楼

HTTP协议非常简单,因此编写HTTP客户端非常简单。
这里是一个
https://github.com/pedro-vicente/lib_netsockets
它使用HTTP GET要从Web服务器检索文件,服务器和文件都是命令行参数。远程文件保存到本地副本。
免责声明:我是作者
检查http.cc
https://github.com/pedro-vicente/lib_netsockets/blob/master/ src / http.cc
int http_client_t::get(const char *path_remote_file)
{
  char buf_request[1024];

  //construct request message using class input parameters
  sprintf(buf_request, "GET %s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n", path_remote_file, m_server_ip.c_str());

  //send request, using built in tcp_client_t socket
  if (this->write_all(buf_request, (int)strlen(buf_request)) < 0)
  {
    return -1;
  }

编辑:已编辑的URL

评论


指定的网址无效。

–Shravan40
16 Dec 16'在11:18

#16 楼

您可以使用EmbeddedRest库。它是轻量级的仅标头库。因此,很容易将其包含在您的项目中,并且不需要编译,因为其中没有.cpp文件。
从repo向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
16年8月4日在4:05

@uhfocuz该库是为iOS和Android编写的。但是我可以帮助您为Win32编译它。不太难

– fnc12
16年8月4日在6:30

我喜欢它的使用强度,它在Mac上可以正常编译,但是在Windows上,它们的库是不同的,例如您没有netdb.h等,所以我想寻求帮助

– uhfocuz
16年8月4日在6:32

@uhfocuz,您要做的就是添加#ifdef _WIN32之类的条件,并在其中添加Windows特定代码。看这里-Unix套接字和Windows套接字之间没有太大区别。我看到两个主要区别:1)首先调用WSAStartup和2)使用closesocket而不是close

– fnc12
16年8月4日在6:38

@uhfocuz请在我的仓库中创建一个问题-我将在有足够时间的时候添加win32支持

– fnc12
16年8月4日在13:53

#17 楼

这是一些(相对)简单的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);
}


#18 楼

通常,我会建议一些跨平台的东西,例如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();


#19 楼

如果您正在寻找多个平台(Linux,Windows和Mac)支持的C ++ HTTP客户端库,以使用Restful Web服务。您可以使用以下选项。



QT网络库-允许应用程序发送网络请求并接收答复

C ++ REST SDK-新兴具有PPL支持的第三方HTTP库

Libcurl-它可能是原生世界中最常用的http库之一。


#20 楼

虽然有点晚。您可能更喜欢https://github.com/Taymindis/backcurl。

它允许您在移动c ++开发中进行http调用。适用于手机游戏开发

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


#21 楼

您可以使用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;
}


#22 楼

我刚刚找到了MS的CppRest SDK,大约1/2小时后,我的第一个简单的Web服务调用开始工作。与这里提到的其他内容相比,经过数小时的浏览后,我什至都无法安装任何东西,我会说这非常令人印象深刻
https://github.com/microsoft/cpprestsdk
向下滚动并单击“文档”,然后单击“入门指南”,您将立即运行一个简单的应用程序。