网络编程:C++REST SDK简析

http_client代码示例

#include 
#ifdef _WIN32
#include 
#include 
#endif 
#include 

using namespace utility;
using namespace web::http;
using namespace web::http::client;
using namespace std::cerr;
using namespace std::endl;

#ifdef _WIN32 //根据平台来定义tcout,确保多语言的文字能够正确输出
#define tcout std::wcout;
#else
#define tcout std::cout;
#endif

//从http_response中取出头部的字符串表示
auto get_headers(http_response resp) {
      
	auto headers = resp.to_string();
	auto end = headers.find("\r\n\r\n");
	if (end != string_t::npos) {
     
		headers.resize(end + 4);
	}
	return headers;
}

auto get_request(string_t uri) {
     
	http_client client{
      uri };
	//用GET的方式发起一个客户端请求
	//并使用then方法串联了两个下一步动作
	//http_client::request的返回值是pplx::task.then是pplx::task类模板的成员函数,参数是能接受其类型参数对象的函数对象,
	//除了最后一个then,其他每个then里都应该返回一个pplx::task,而task的内部类型,就是下一个then块里函数对象接受的参数类型。
	auto request = client.request(methods::GET).then([](http_response resp) {
     
		if (resp.status_code() != status_codes::OK) {
     
			//不OK,显示当前响应信息
			auto headers = get_headers(resp);
			tcout << headers;
		}
		//进一步取出完整响应
		return resp.extract_string();
		}).then([](string_t str) {
     
			//输出到终端
			tcout << str;
			});
		return request;
}
#ifdef _WIN32 //根据平台定义合适的程序入口
int wmian(int argc, wchar_t* argv[])
#else
int main(int argc, char* argv[])
#endif 
{
     
#ifdef _WIN32
	_setmode(_fileno(stdout), _O_WTEXT);
#endif
	if (argc != 2) {
     
		cerr << "A URL is needed!\n";
		return 1;
	}
	//等待请求及其关联处理全部完成
	try {
     
		auto request = get_request(argv[1]);
		request.wait();
	}
	//处理请求过程中产生的异常
	catch (const std::exception& e){
     
		cerr << "Error exception " << e.what() << endl;
		return 1;
	}
}



C++REST SDK是用来开发http客户端和服务器的现代异步C++代码库,支持http服务器,http客户端,异步流,任务,JSON,URI,websocket客户端。

异步流
C++REST SDK实现了一套异步流,能够实现对文件的异步读写。
以下代码展示了把网络请求响应异步存储到文件result.html中

#include 
#include 
#ifdef _WIN32
#include 
#include 
#endif 
#include 
#include 
#include 

using namespace utility;
using namespace web::http;
using namespace web::http::client;
using namespace concurrency::streams;
using namespace std::cerr;
using namespace std::endl;

#ifdef _WIN32 //根据平台来定义tcout,确保多语言的文字能够正确输出
#define tcout std::wcout;
#else
#define tcout std::cout;
#endif

//从http_response中取出头部的字符串表示
auto get_headers(http_response resp) {
      
	auto headers = resp.to_string();
	auto end = headers.find("\r\n\r\n");
	if (end != string_t::npos) {
     
		headers.resize(end + 4);
	}
	return headers;
}

auto get_request(string_t uri) {
     
	http_client client{
      uri };
	//用GET的方式发起一个客户端请求
	//并使用then方法串联了两个下一步动作
	//http_client::request的返回值是pplx::task.then是pplx::task类模板的成员函数,参数是能接受其类型参数对象的函数对象,
	//除了最后一个then,其他每个then里都应该返回一个pplx::task,而task的内部类型,就是下一个then块里函数对象接受的参数类型。
	auto request = client.request(methods::GET)
		.then([](http_response resp) {
     
		if (resp.status_code() == status_codes::OK) {
     
			//正常的话
			tcout << U("Saving ...\n");
			ostream fs;
			fstream::open_ostream(
				U("result.html"), std::ios_base::out | std::ios_base::trunc
			).then([&fs, resp](ostream os) {
     
				fs = os;
				//读取网页内容到流
				return resp.body().read_to_end(fs.streambuf());
				}).then([&fs](size_t size) {
     
					fs.close();
					tcout << size << U(" bytes  saved\n");
					}).wait();
		}
		else {
     
			//否则显示当前响应信息
			auto headers = get_headers(resp);
			tcout << headers;
			tcout << resp.extract_string().get();
		}
		});
		return request;
}
#ifdef _WIN32 //根据平台定义合适的程序入口
int wmian(int argc, wchar_t* argv[])
#else
int main(int argc, char* argv[])
#endif 
{
     
#ifdef _WIN32
	_setmode(_fileno(stdout), _O_WTEXT);
#endif
	if (argc != 2) {
     
		cerr << "A URL is needed!\n";
		return 1;
	}
	//等待请求及其关联处理全部完成
	try {
     
		auto request = get_request(argv[1]);
		request.wait();
	}
	//处理请求过程中产生的异常
	catch (const std::exception& e){
     
		cerr << "Error exception " << e.what() << endl;
		return 1;
	}
}



C++REST SDK的对象大部分都是基于shared_ptr实现的,因而可以轻松大胆的进行复制

JSON支持
C++REST SDK对JSON有很好的支持,JSON的基本类型有空值类型,布尔类型,数字类型和字符串类型,复合类型是数组(array)和对象(object)
在C++REST SDK里,核心类型是web::json::value

C++REST SDK里的http_request 和http_response对JSON有原生支持,如可以使用extract_json成员函数来异步提取http请求或响应体中的JSON内容

http服务器
C++ REST SDK的http_listener会通过调用Boost.Asio和操作系统的底层接口(IOCP,epoll)来完成功能,向使用者隐藏这些细节,提供一个简单的编程接口

//简单rest服务器示例代码,处理sayHi请求
#include 
#include 
#include 
#include 
#ifdef _WIN32
#include 
#include 
#endif
#include 
#include 

using namespace std;
using namespace utility;
using namespace web;
using namespace web::http;
using namespace web::http::experimental::listener;

#ifdef _WIN32
#define tcout std::wcout;
#else
#define tcout std::cout;
#endif

void handle_get(http_request req) {
     
	//http_request::request_uri函数返回的是uri的引用,因而用auto&来接收
	auto& uri = req.request_uri();
	if (uri.path() != U("/sayHi")) {
     
		req.reply(status_codes::NotFound);
		return;
	}
	tcout << uri::decode(uri.query()) << endl;

	auto query = uri::split_query(uri.query());
	auto it = query.find(U("name"));
	if (it == query.end()) {
     
		req.reply(status_codes::BadRequest, U("Missing query info"));
		return;
	}

	//http_request::reply的第二个参数是json::value类型,这会让HTTP的Content-Type自动设置成 application/json
	auto answer = json::value::object(true);
	answer[U("msg")] = json::value(string_t(U("Hi, ")) + uri::decode(it->second) + U("!"));
	req.reply(status_codes::OK, answer);
}

int main() {
     
#ifdef _WIN32
	_setmode(_fileno(stdout), _O_WTEXT);
#endif
	http_listener listener(U("http://127.0.0.1:8080/"));
	listener.support(methods::GET, handle_get);
	try {
     
		listener.open().wait();
		tcout << "Listener. Press Enter to exit.\n";
		string line;
		getline(cin, line);
		listener.close().wait();
	}
	catch (const exception& e) {
     
		cerr << e.what() << endl;
		return 1;
	}
}

C++REST SDK使用异步的编程模式,使得写不阻塞的代码变得很容易,底层它是用一个线程池实现的,缺省会开启40个线程,如果使用完了,会导致系统阻塞,线程数量代码中可控

//设置线程池大小为10
#include 
crossplat::threadpool::initialize_with_threads(10);

C++REST服务器应当增加线程池大小,并且对并发数量进行统计,在并发数接近线程池大小时拒绝新的连接,一般可返回status_codes::ServiceUnavailable,以避免系统阻塞

你可能感兴趣的