1、WebsocketPP简介
最近需要构建一个本地的服务器程序,处理PHP服务器发来的请求,看到微软的一个开源项目内部用到了这个开源库,于是就试着用了下,效果还好,主要是很容易处理Web请求并返回数据。(C++写程序来请求,然后用C++在WebsocketPP构建服务器处理请求,想怎么定义数据怎么加密都可以,因为都是C++程序,也不用学习PHP,想想就很美好)。
WebsocketPP是一个使用C++编写的开源Web服务器框架,具体实现则是用的大名鼎鼎的boost::asio。ASIO是一个跨平台的网络库,Windows上底层实现使用的是重叠I/O(PS:Windows上socket服务器性能最好的当然是IOCP了),其架构设计如下图:
ASIO的官方地址:http://think-async.com/Asio/。WebSocketPP的官方地址:http://www.zaphoyd.com/websocketpp/。由于依耐性,使用前你还需要去下载并编译boost库,编译后把对应的目录加入到VS中。
2、使用WebsocketPP
去Github上把压缩包下载下来,里面很多例子可以参考,因为涉及到很多类型(服务器、客户端、请求、返回……)。我也就用了下处理web请求,返回Json数据。
VS新建一个工程,然后把websocketpp这个文件夹都拷贝过去,把所有文件都添加到VS中。添加头文件,创建一个扩展的服务器类……看代码
#include "stdafx.h"
#include
#include
#include
using std::string;
using std::wstring;
struct testee_config : public websocketpp::config::asio {
// pull default settings from our core config
typedef websocketpp::config::asio core;
typedef core::concurrency_type concurrency_type;
typedef core::request_type request_type;
typedef core::response_type response_type;
typedef core::message_type message_type;
typedef core::con_msg_manager_type con_msg_manager_type;
typedef core::endpoint_msg_manager_type endpoint_msg_manager_type;
typedef core::alog_type alog_type;
typedef core::elog_type elog_type;
typedef core::rng_type rng_type;
typedef core::endpoint_base endpoint_base;
static bool const enable_multithreading = true;
struct transport_config : public core::transport_config {
typedef core::concurrency_type concurrency_type;
typedef core::elog_type elog_type;
typedef core::alog_type alog_type;
typedef core::request_type request_type;
typedef core::response_type response_type;
static bool const enable_multithreading = true;
};
typedef websocketpp::transport::asio::endpoint
transport_type;
static const websocketpp::log::level elog_level =
websocketpp::log::elevel::all;
static const websocketpp::log::level alog_level =
websocketpp::log::alevel::all;
};
typedef websocketpp::server server;
using websocketpp::lib::placeholders::_1;
using websocketpp::lib::placeholders::_2;
using websocketpp::lib::bind;
// pull out the type of messages sent by our config
typedef server::message_ptr message_ptr;
// Define a callback to handle incoming messages
void on_message(server* s, websocketpp::connection_hdl hdl, message_ptr msg) {
s->send(hdl, msg->get_payload(), msg->get_opcode());
}
void on_socket_init(websocketpp::connection_hdl, boost::asio::ip::tcp::socket & s) {
boost::asio::ip::tcp::no_delay option(true);
s.set_option(option);
}
void on_http(server* s, websocketpp::connection_hdl hdl)
{
server::connection_ptr con = s->get_con_from_hdl(hdl);
websocketpp::http::parser::request rt = con->get_request();
const string& strUri = rt.get_uri();
const string& strMethod = rt.get_method();
const string& strBody = rt.get_body();
const string& strVersion = rt.get_version();
std::cout<<"接收到一个"<set_body("everything is ok now!");
con->set_status(websocketpp::http::status_code::value(600));//websocketpp::http::status_code::ok
websocketpp::http::parser::response rp;
string strContent = rt.raw();
rp.consume(strContent.c_str(), strContent.size());
//if ( strMethod.compare("POST") == 0 )
{
websocketpp::http::parser::request r;
size_t nRet = r.consume(strUri.c_str(), strUri.size());
int k = 0;
}
}
int main(int argc, char * argv[]) {
// Create a server endpoint
// websocketpp::http::parser::request r;
// r.set_uri("http://www.baidu.com");
// r.set_method("GET");
// r.set_version("HTTP/1.1");
// r.set_body("/?s=123");
// string str = r.raw();
// std::string firefox = "GET / HTTP/1.1\r\nHost: localhost:5000\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:10.0) Gecko/20100101 Firefox/10.0\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nAccept-Language: en-us,en;q=0.5\r\nAccept-Encoding: gzip, deflate\r\nConnection: keep-alive, Upgrade\r\nSec-WebSocket-Version: 8\r\nSec-WebSocket-Origin: http://zaphoyd.com\r\nSec-WebSocket-Key: pFik//FxwFk0riN4ZiPFjQ==\r\nPragma: no-cache\r\nCache-Control: no-cache\r\nUpgrade: websocket\r\n\r\n";
//
// size_t n = r.consume(firefox.c_str(), firefox.size());
server testee_server;
short port = 1990;
size_t num_threads = 1;
if (argc == 3) {
port = atoi(argv[1]);
num_threads = atoi(argv[2]);
}
try {
// Total silence
testee_server.clear_access_channels(websocketpp::log::alevel::all);
testee_server.clear_error_channels(websocketpp::log::alevel::all);
// Initialize ASIO
testee_server.init_asio();
testee_server.set_reuse_addr(true);
// Register our message handler
testee_server.set_message_handler(bind(&on_message,&testee_server,::_1,::_2));
testee_server.set_socket_init_handler(bind(&on_socket_init,::_1,::_2));
testee_server.set_http_handler(bind(&on_http, &testee_server, ::_1));
// Listen on specified port with extended listen backlog
testee_server.set_listen_backlog(8192);
testee_server.listen(port);
// Start the server accept loop
testee_server.start_accept();
// Start the ASIO io_service run loop
if (num_threads == 1) {
testee_server.run();
} else {
typedef websocketpp::lib::shared_ptr thread_ptr;
std::vector ts;
for (size_t i = 0; i < num_threads; i++) {
ts.push_back(websocketpp::lib::make_shared(&server::run, &testee_server));
}
for (size_t i = 0; i < num_threads; i++) {
ts[i]->join();
}
}
} catch (websocketpp::exception const & e) {
std::cout << "exception: " << e.what() << std::endl;
}
}
on_http就是处理web请求的回调,在这里我们可以根据请求名称来响应处理。const string& strUri = rt.get_uri();获取的就是请求的名称,比如设置服务器端口号为1200,请求:localhost:1200/hello,那么strUri就是/hello,也就是说我们可以根据字符串的匹配来处理不同的请求名称。还有就是POST/GET请求,const string& strMethod = rt.get_method()获取的就是请求方法,POST对应的strMethod =“POST”,GET对应的就是"GET"。请求的数据,const string& strBody = rt.get_body(),只有POST请求才可能有发送的数据,GET请求时这里为空。const string& strVersion = rt.get_version()获取的是HTTP的版本号,通常是1.1。
con->set_body("everything is ok now!");con->set_status(websocketpp::http::status_code::value(600));这两句是设置返回数据和返回码。
设置服务器监听端口testee_server.listen(port),端口号被占用时,开启服务器会失败,如何捕捉呢?在catch (websocketpp::exception const & e)中。
吐槽下CSDN的文章编辑器,真是垃圾。格式设置好痛苦,我复制文字后得粘贴到记事本里去掉格式然后再复制粘贴回来。
下载源代码:http://download.csdn.net/detail/mfcing/9313755(VS2010工程)