有没有办法轻松用C++发出HTTP请求?具体来说,我想下载页面的内容(API)并检查内容,看它是否包含1或0.是否也可以将内容下载到字符串中?
我有同样的问题.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#include // 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美分......
Windows代码:
#include#include #include #include #include #include #include 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< 这是一个更好的实现:
#include#include #include 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); }
@JuanLuisSoldi我想你真的需要成为Windows开发人员才能欣赏这段代码的"美丽"......
我无法让curlpp工作,所以我使用了你的代码,谢谢
美丽.这应该是公认的答案.
@ Expanding-Dev只有MSVC(visual studio)理解"pragma评论".如果您使用其他任何东西,您必须手动链接"ws2_32.lib"(与任何其他库一样).
3> Homer6..:在Linux上,我尝试了cpp-netlib,libcurl,curlpp,urdl,boost :: asio并考虑了Qt(但根据许可证将其关闭).所有这些对于这种用途来说都是不完整的,有稀疏的接口,文档很差,没有维护或不支持https.
然后,根据/sf/ask/17360801/的建议,我尝试了POCO.哇,我希望我多年前见过.以下是发出HTTP GET请求的示例:
http://xjia.heroku.com/2011/09/10/learning-poco-get-with-http/
POCO是免费的,开源的(增强许可证).不,我与公司没有任何关系; 我真的很喜欢他们的界面.干得好(和女孩们).
http://pocoproject.org/download/index.html
希望这有助于某人...我花了三天时间尝试所有这些库.
4> huu..:有一种更新的,不太成熟的卷曲包装器正在开发,称为C++请求.这是一个简单的GET请求:
#include#include int main(int argc, char** argv) { auto response = cpr::Get(cpr::Url{"http://httpbin.org/get"}); std::cout << response.text << std::endl; } 它支持各种HTTP动词和卷曲选项.还有更多的使用文档这里.
免责声明:我是这个库的维护者.
我昨天参加了你的CppCon 2015闪电演讲.做得好 - 谈话和图书馆.我特别喜欢"Curl for people"的设计理念.
@Sossenbinder,如果您熟悉CMake,则可以使用该文件为该项目生成Visual Studio构建文件。[appveyor配置](https://github.com/whoshuu/cpr/blob/3de75778fc225593281cba75b1a77a614d82618b/appveyor.yml)文件包含如何完成此操作的粗略示例。
看起来不错,但构建是地狱,所以你的lib是没用的,我不能依赖包管理器(需要可靠的方法如何在外部添加deps)并且需要尽快使用功能库...
5> FreeMemory..: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.所有这些都包含在上面链接的卷曲教程中.
6> Marcelo Sant..:如您所希望的C++解决方案,您可以使用Qt.它有一个你可以使用的QHttp类.
您可以查看文档:
http->setHost("qt.nokia.com"); http->get(QUrl::toPercentEncoding("/index.html"));Qt还有更多可用于常见C++应用程序的内容.
我认为QHttp已被Qt 4.6及更高版本中的QNetworkAccessManager和相关类所取代.
自Qt 4.4以来,已经记录了`QNetworkAccessManager`; 在Qt 4.8中说:`QHttp - 这个类已经过时了.它用于保持旧的源代码工作.我们强烈建议不要在新代码中使用它.所以我猜它仍然可用,如果你忽略了已弃用的警告.
7> Mark Lakata..:这是我在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");课程实施:
#includeclass 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 (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; } };
8> 小智..:您可能需要检查C++ REST SDK(代号为"Casablanca").http://msdn.microsoft.com/en-us/library/jj950081.aspx
使用C++ REST SDK,您可以更轻松地从C++应用程序连接到HTTP服务器.
用法示例:
#include#include 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设计在本机代码中进行基于云的客户端 - 服务器通信.
9> Vinz..:有了这个答案,我将参考Software_Developer的答案.通过重建代码,我发现某些部分已被弃用(
gethostbyname()
)或者没有为操作提供错误处理(创建套接字,发送内容).以下Windows代码使用Visual Studio 2013和Windows 8.1 64位以及Windows 7 64位进行测试.它将以www.google.com的Web服务器为目标进行IPv4 TCP连接.
#include#include #include #include #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> sybreon..:C++没有提供任何直接的方法.它完全取决于您拥有的平台和库.
在最坏的情况下,您可以使用boost :: asio库建立TCP连接,发送HTTP头(RFC 2616),并直接解析响应.看看您的应用程序需求,这很简单.