你如何使用C ++进行HTTP请求?

有什么方法可以轻松地使用C ++进行HTTP请求? 具体来说,我想下载一个页面(一个API)的内容,并检查内容,看看它是否包含1或0.也可以下载内容到一个字符串?

我有同样的问题。 libcurl确实是完整的。 有一个C ++包装curlpp可能会让你感兴趣,因为你要求一个C ++库。 氖是另一个有趣的C库,也支持webdav。

curlpp看起来很自然,如果你使用C ++。 源代码分发中提供了许多示例。 为了获得URL的内容,你需要做一些类似的事情(从示例中提取):

// Edit : rewritten for cURLpp 0.7.3 // Note : namespace changed, was cURLpp in 0.7.2 ... #include <curlpp/cURLpp.hpp> #include <curlpp/Options.hpp> // RAII cleanup curlpp::Cleanup myCleanup; // Send request and get a result. // Here I use a shortcut to get it in a string stream ... std::ostringstream os; os << curlpp::options::Url(std::string("http://www.wikipedia.org")); string asAskedInQuestion = os.str(); 

看看curlpp源码分发中的examples目录,还有很多更复杂的情况。

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

在Linux上,我尝试了cpp-netlib,libcurl,curlpp,urdl,boost :: asio并考虑了Qt(但是根据许可证将其关闭)。 所有这些对于这个用途来说都是不完整的,界面不够流畅,文档很差,没有维护或者不支持https。

然后,在https://stackoverflow.com/a/1012577/278976的建议,我尝试了POCO。; 哇,我希望我多年前见过。 下面是一个制作HTTP GET请求的例子:

http://xjia.heroku.com/2011/09/10/learning-poco-get-with-http/

POCO是免费的,开源(升压许可证)。 不,我和公司没有任何关系。 我真的很喜欢他们的界面。 伟大的工作人员(和gals)。

http://pocoproject.org/download/index.html

希望这可以帮助别人…花了我三天的时间来尝试所有这些库。

有一个更新,不太成熟的卷曲包装被称为C ++请求 。 这是一个简单的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动词和卷曲选项。 这里有更多的使用文档。

免责声明:我是这个图书馆的维护者

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

您可以查看文档 :

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

Qt还有更多,你可以在一个普通的C ++应用程序中使用。

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的最小包装,以便能够将网页作为字符串获取。 例如,这对单元测试很有用。 它基本上是围绕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; } }; 

有了这个答案,我参考了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()的返回值

C ++不提供任何直接执行的方法。 这完全取决于你有什么样的平台和库。

在最坏的情况下,您可以使用boost :: asio库建立一个TCP连接,发送HTTP头(RFC 2616),并直接解析响应。 看看你的应用程序需求,这很简单。

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

使用C ++ REST SDK(代号“Casablanca”),您可以更轻松地从C ++应用程序连接到HTTP服务器。

C ++ REST SDK(代号“Casablanca”)是一个使用现代异步C ++ API设计的本地代码中用于基于云的客户端 – 服务器通信的Microsoft项目。

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

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

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

另外,对于Linux, 这是一个简单的HTTP客户端。 您可以实现自己的简单的HTTP GET客户端,但是如果涉及到身份验证或重定向,或者您需要在代理之后工作,这将不起作用。 对于这些情况,您需要像libcurl这样的完整的库。

对于libcurl的源代码, 这是最接近你想要的(Libcurl有很多例子 )。 看看主要的功能。 在成功连接之后,html内容将被复制到缓冲区。 用你自己的函数替换parseHtml。

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

一般来说,我会推荐像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.comhttp://img.dovov.comsrpr/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(); 

虽然有点晚了 你可能更喜欢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 

HTTP协议非常简单,所以编写HTTP客户端非常简单。 这是一个

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

它使用HTTP GET从Web服务器检索文件,服务器和文件都是命令行参数。 远程文件保存到本地副本。

免责声明:我是作者

编辑:编辑的网址

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