想找一个轻量级、跨平台的C++网络请求函数库,然而折腾了一天也不如意。老规矩,没找到就自己动手吧。
本篇代码以GET、POST请求的方式封装了boost.asio提供的socket网络连接,返回的是std::string类型。
如果获取的json字符串之类,直接用就可以了。下一篇将奉上图片等格式的本地保存功能,尽情期待。
当然,关于boost的安装和配置还是要费一番周折的,本人用的是Mac系统,具体安装配置过程可参考:
http://blog.csdn.net/ym19860303/article/details/10155751
博文首发:http://blog.csdn.net/duzixi
NetworkRequest.h
// // NetworkRequest.h // // Created by duzixi.com on 15/8/25. // #include <stdio.h> #include <iostream> #include <fstream> #include "json.h" #include "boost/asio.hpp" using namespace std; /// GET请求 string GetRequest(char* host, char* path); string GetRequest(string url); /// POST请求 string PostRequest(char* host, char* path, string form);
NetworkRequest.cpp
// // NetworkRequest.cpp // // Created by duzixi.com on 15/8/25. // #include "NetworkRequest.h" using namespace std; using boost::asio::ip::tcp; /// POST请求 string PostRequest(char* host, char* path, string form) { long length = form.length(); // 声明Asio基础: io_service(任务调度机) boost::asio::io_service io_service; // 获取服务器终端列表 tcp::resolver resolver(io_service); tcp::resolver::query query(host,"http"); tcp::resolver::iterator iter = resolver.resolve(query); // 尝试连接每一个终端,直到成功建立socket连接 tcp::socket socket(io_service); boost::asio::connect(socket, iter); // 构建网络请求头 // 指定 "Connection: close" 在获取应答后断开连接,确保获文件全部数据。 boost::asio::streambuf request; ostream request_stream(&request); request_stream << "POST " << path << " HTTP/1.1\r\n"; request_stream << "Host: " << host << "\r\n"; request_stream << "Accept: */*\r\n"; request_stream << "Content-Type:application/x-www-form-urlencoded\r\n"; request_stream << "Content-Length: " << length << "\r\n"; request_stream << "Connection: close\r\n\r\n"; // 注意这里是两个空行 request_stream << form; //POST 发送的数据本身不包含多于空行 // 发送请求 boost::asio::write(socket, request); // 读取应答状态. 应答缓冲流 streambuf 会自动增长至完整的行 // 该增长可以在构造缓冲流时通过设置最大值限制 boost::asio::streambuf response; boost::asio::read_until(socket, response, "\r\n"); // 检查应答是否OK. istream response_stream(&response);// 应答流 string http_version; response_stream >> http_version; unsigned int status_code; response_stream >> status_code; string status_message; getline(response_stream, status_message); if (!response_stream || http_version.substr(0, 5) != "HTTP/") { printf("无效响应\n"); } if (status_code != 200) { printf("响应返回 status code %d\n", status_code); } // 读取应答头部,遇到空行后停止 boost::asio::read_until(socket, response, "\r\n\r\n"); // 显示应答头部 string header; int len = 0; while (getline(response_stream, header) && header != "\r") { if (header.find("Content-Length: ") == 0) { stringstream stream; stream << header.substr(16); stream >> len; } } long size = response.size(); if (size > 0) { // .... do nothing } // 循环读取数据流,直到文件结束 boost::system::error_code error; while (boost::asio::read(socket, response, boost::asio::transfer_at_least(1), error)) { // 获取应答长度 size = response.size(); if (len != 0) { cout << size << " Byte " << (size * 100) / len << "%\n"; } } if (error != boost::asio::error::eof) { throw boost::system::system_error(error); } cout << size << " Byte 内容已下载完毕." << endl; // 将streambuf类型转换为string类型返回 istream is(&response); is.unsetf(ios_base::skipws); string sz; sz.append(istream_iterator<char>(is), istream_iterator<char>()); // 返回转换后的字符串 return sz; } /// GET请求 string GetRequest(char* host, char* path) { // 声明Asio基础: io_service(任务调度机) boost::asio::io_service io_service; // 获取服务器终端列表 tcp::resolver resolver(io_service); tcp::resolver::query query(host,"http"); tcp::resolver::iterator iter = resolver.resolve(query); // 尝试连接每一个终端,直到成功建立socket连接 tcp::socket socket(io_service); boost::asio::connect(socket, iter); // 构建网络请求头. // 指定 "Connection: close" 在获取应答后断开连接,确保获文件全部数据。 boost::asio::streambuf request; ostream request_stream(&request); request_stream << "GET " << path << " HTTP/1.1\r\n"; request_stream << "Host: " << host << "\r\n"; request_stream << "Accept: */*\r\n"; request_stream << "Connection: close\r\n\r\n"; // 发送请求 boost::asio::write(socket, request); // 读取应答状态. 应答缓冲流 streambuf 会自动增长至完整的行 // 该增长可以在构造缓冲流时通过设置最大值限制 boost::asio::streambuf response; boost::asio::read_until(socket, response, "\r\n"); // 检查应答是否OK. istream response_stream(&response); string http_version; response_stream >> http_version; unsigned int status_code; response_stream >> status_code; string status_message; getline(response_stream, status_message); if (!response_stream || http_version.substr(0, 5) != "HTTP/") { printf("响应无效\n"); } if (status_code != 200) { printf("响应返回 status code %d\n", status_code); } // 读取应答头部,遇到空行后停止 boost::asio::read_until(socket, response, "\r\n\r\n"); // 显示应答头部 string header; int len = 0; while (getline(response_stream, header) && header != "\r") { if (header.find("Content-Length: ") == 0) { stringstream stream; stream << header.substr(16); stream >> len; } } long size = response.size(); if (size > 0) { // ... do nothing ... } boost::system::error_code error; // 读取错误 // 循环读取数据流,直到文件结束 while (boost::asio::read(socket, response, boost::asio::transfer_at_least(1), error)) { // 获取应答长度 size = response.size(); if (len != 0) { cout << size << " Byte " << (size * 100) / len << "%" << endl; } } // 如果没有读到文件尾,抛出异常 if (error != boost::asio::error::eof) { throw boost::system::system_error(error); } cout << size << " Byte 内容已下载完毕." << endl; // 将streambuf类型转换为string类型,并返回 istream is(&response); is.unsetf(ios_base::skipws); string sz; sz.append(istream_iterator<char>(is), istream_iterator<char>()); return sz; } /// GET请求(重载) string GetRequest(string url) { size_t index; // 去掉url中的协议头 if (url.find("http://") != string::npos ) { url = url.substr(7); } printf("url:%s\n", url.c_str()); // 截取host字符串 index = url.find("/"); char* host = new char[index]; strcpy(host, url.substr(0, index).c_str()); // 截取urlPath字符串 char* urlPath = new char[url.length() - index + 1]; strcpy(urlPath, url.substr(index, url.length() - index).c_str()); return GetRequest(host, urlPath); }
以前GET、POST请求都是用封好的方法,现在感觉真是太方便了。
第一次自己动手拼接协议头,疏漏之处难免,敬请指正。