- WebSocket ++是一个C ++库,可用于实现WebSocket功能。该项目的目标是提供一种可移植,灵活,轻量级,低级和高性能的WebSocket实现。
- WebSocket ++并不打算单独用作Web应用程序框架或功能全面的Web服务平台。这样的组件,示例和性能调整都适合作为WebSocket客户端或服务器进行操作。有一些最不方便的功能(例如,响应WebSocket升级以外的HTTP请求的能力)偏离了这些,但这些并不是项目的重点。特别是WebSocket ++并不打算实现任何与WebSocket不相关的后备选项(ajax /长轮询/彗星/等)。
- 为了保持紧凑并提高可移植性,WebSocket ++项目致力于在可能和适当的情况下减少或消除外部依赖性。WebSocket ++核心除了C ++ 11标准库外没有其他依赖项。对于非C ++ 11编译器,Boost库为使用的C ++ 11功能提供了polyfill。
- WebSocket ++实现了可插拔数据传输组件(使用两个可以相互替换的网络传输模块,其中一个基于C++ I/O流,另一个基于Asio)。
- 默认组件允许通过使用STL iostream或通过读取和写入char缓冲区的原始字节改组来减少功能。该组件没有非STL依赖关系,可以在没有Boost的C ++ 11环境中使用。
- 基于Asio的传输组件提供了功能齐全的网络客户端/服务器功能。该组件需要Boost Asio或C ++ 11编译器以及独立的Asio。作为高级选项,如果您想使用另一个库提供自己的库,则WebSocket ++支持自定义传输层。
资源:
仓库包含如下几个目录:
WebSocket++的主要特性包括:
系统环境:centos 7
编译器: clion
当前电脑已经安装boost, 具体可以参考boost:从0到1开发boost(linux、clion)
$ git clone https://github.com/zaphoyd/websocketpp.git
$ cd websocketpp #进入目录
$ mkdir build
$ cd build
$ cmake ..
$ sudo make
-- ENABLE_CPP11 = ON
-- BUILD_EXAMPLES = OFF
-- BUILD_TESTS = OFF
-- WEBSOCKETPP_ROOT = /home/oceanstar/workspace/cpp/websocketpp
-- WEBSOCKETPP_BIN = /home/oceanstar/workspace/cpp/websocketpp/build/bin
-- WEBSOCKETPP_LIB = /home/oceanstar/workspace/cpp/websocketpp/build/lib
-- Install prefix = /usr/local
-- WEBSOCKETPP_BOOST_LIBS =
-- WEBSOCKETPP_PLATFORM_LIBS =
-- WEBSOCKETPP_PLATFORM_TLS_LIBS =
-- OPENSSL_FOUND =
-- OPENSSL_INCLUDE_DIR =
-- OPENSSL_LIBRARIES =
$ sudo make install
Install the project...
-- Install configuration: ""
-- Installing: /usr/local/lib/cmake/websocketpp/websocketpp-config.cmake
-- Installing: /usr/local/lib/cmake/websocketpp/websocketpp-configVersion.cmake
-- Installing: /usr/local/include//websocketpp
-- Installing: /usr/local/include//websocketpp/base64
-- Installing: /usr/local/include//websocketpp/base64/base64.hpp
-- Installing: /usr/local/include//websocketpp/client.hpp
-- Installing: /usr/local/include//websocketpp/close.hpp
-- Installing: /usr/local/include//websocketpp/common
$ cd websocketpp/examples/echo_server
$ g++ -o echo_server echo_server.cpp -lboost_system -lpthread -std=c++11
$ ./echo_server
$ cd websocketpp/examples/echo_client
$ g++ -o echo_client echo_client.cpp -lboost_system -lpthread -std=c++11
当然,我们也可以使用在线测试工具测试: http://coolaf.com/tool/chattest
要使用webSocket++,必须要加入一些依赖项
c++ -std=c++11 step1.cpp (Asio Standalone)
c++ -std=c++11 step1.cpp -lboost_system (Boost Asio)
#include
#include
#include
#include
#include
#include
void test0(){
typedef websocketpp::log::basic<websocketpp::concurrency::none,websocketpp::log::alevel> access_log;
std::stringstream out;
access_log logger(0xffffffff,&out);
logger.clear_channels(0xffffffff);
logger.write(websocketpp::log::alevel::devel,"devel");
// std::cout << "|" << out.str() << "|" << std::endl;
assert( out.str().size() == 0 );
}
void test1(){
typedef websocketpp::log::basic<websocketpp::concurrency::none,websocketpp::log::alevel> access_log;
std::stringstream out;
access_log logger(0xffffffff,&out);
logger.set_channels(0xffffffff);
logger.write(websocketpp::log::alevel::devel,"devel");
// std::cout << "|" << out.str() << "|" << std::endl;
assert( out.str().size() > 0 );
}
void test_copy_constructor1 (){
typedef websocketpp::log::basic<websocketpp::concurrency::basic,websocketpp::log::alevel> basic_access_log_type;
std::stringstream out;
basic_access_log_type logger1(0xffffffff,&out);
basic_access_log_type logger2(logger1);
//basic_access_log_type logger2(std::move(logger1)); // move_constructor
logger2.set_channels(0xffffffff);
logger2.write(websocketpp::log::alevel::devel,"devel");
assert( out.str().size() > 0 );
}
void test_copy_constructor2 (){
typedef websocketpp::log::basic<websocketpp::concurrency::basic,websocketpp::log::alevel> basic_access_log_type;
std::stringstream out;
basic_access_log_type logger1(0xffffffff,&out);
basic_access_log_type logger2(logger1);
logger2.set_channels(0xffffffff);
logger1.write(websocketpp::log::alevel::devel,"devel");
assert( out.str().size() == 0 );
}
int main(){
test0();
test1();
test_copy_constructor1();
test_copy_constructor2();
}
#include
#include
#include
#include
#include
// ----- -lssl -lcrypto ----------------
void test_construct_server_asio_tls (){
websocketpp::server<websocketpp::config::asio_tls> s;
s.init_asio();
}
void test_test_construct_server_asio1(){
websocketpp::server<websocketpp::config::asio> s;
s.init_asio();
}
void test_test_construct_server_asio2(){
websocketpp::server<websocketpp::config::asio> s;
boost::asio::io_service ios;
s.init_asio(&ios);
}
void test_move_construct_server_core(){
websocketpp::server<websocketpp::config::core> s1;
websocketpp::server<websocketpp::config::core> s2(std::move(s1));
}
void test_listen_after_listen_failure (){
using websocketpp::transport::asio::error::make_error_code;
using websocketpp::transport::asio::error::pass_through;
websocketpp::server<websocketpp::config::asio> server1;
websocketpp::server<websocketpp::config::asio> server2;
websocketpp::lib::error_code ec;
server1.init_asio();
server2.init_asio();
boost::asio::ip::tcp::endpoint ep1(boost::asio::ip::address::from_string("127.0.0.1"), 12345);
boost::asio::ip::tcp::endpoint ep2(boost::asio::ip::address::from_string("127.0.0.1"), 23456);
server1.listen(ep1, ec);
assert(!ec);
server2.listen(ep1, ec); // error: [2021-02-04 10:40:35] [info] asio listen error: system:98 (Address already in use)
assert(ec);
server2.listen(ep2, ec);
assert(!ec);
}
struct endpoint_extension{
endpoint_extension() : extension_value(5) {}
int extension_method() {
return extension_value;
}
bool is_server() const {
return false;
}
int extension_value;
};
struct stub_config : public websocketpp::config::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::transport_type transport_type;
typedef endpoint_extension endpoint_base;
};
void test_endpoint_extensions (){
websocketpp::server<stub_config> s;
assert(s.extension_value == 5);
assert(s.extension_method() == 5);
assert(s.is_server());
}
int main(){
test_construct_server_asio_tls();
test_test_construct_server_asio1();
test_test_construct_server_asio2();
test_move_construct_server_core();
test_listen_after_listen_failure();
}
#include
#include
#include
#include
void test_uri_valid(){
websocketpp::uri uri("ws://localhost:9000/chat");
assert(uri.get_valid());
assert(!uri.get_secure());
assert(uri.get_scheme() == "ws");
assert(uri.get_host() == "localhost");
assert(uri.get_port() == 9000);
assert(uri.get_port_str() == "9000");
assert(uri.get_resource() == "/chat");
assert(uri.get_query().empty());
assert(uri.str() == "ws://localhost:9000/chat");
assert(uri.get_authority() == "localhost:9000");
assert(uri.get_host_port() == "localhost:9000");
websocketpp::uri uri1("ws://127.0.0.1/chat");
assert(uri1.get_valid());
assert(!uri.get_secure());
assert(uri1.get_host() == "127.0.0.1");
assert(uri1.get_port_str() == "80");
assert(uri1.get_resource() == "/chat");
websocketpp::uri uri2("wss://localhost/chat");
assert(uri2.get_valid());
assert(uri2.get_secure());
assert(uri2.get_scheme() == "wss");
assert(uri2.get_port() == 443);
assert(uri2.get_resource() == "/chat");
websocketpp::uri uri3("wss://localhost:9000");
assert(uri3.get_port() == 9000);
assert(uri3.get_resource() == "/");
// Valid URI IPv6 Literal
websocketpp::uri uri4("wss://[::1]:9000/chat");
assert(uri4.get_host() == "::1");
websocketpp::uri uri5("wss://thor-websocket.zaphoyd.net:88/");
assert(uri5.get_host() == "thor-websocket.zaphoyd.net");
// Invalid URI (port too long)
websocketpp::uri uri6("wss://localhost:900000/chat");
assert(!uri6.get_valid());
websocketpp::uri uri7("http://localhost:9000/chat");
assert(uri7.get_valid());
websocketpp::uri uri8("wss://localhost:9000/chat/foo/bar");
assert(uri8.get_resource() == "/chat/foo/bar");
// Invalid URI includes uri fragment
websocketpp::uri uri9("wss:/localhost:9002/chat#foo");
assert(!uri9.get_valid());
}
int main(){
test_uri_valid();
}
此处为[md_tutorials_utility_server_utility_server](https://docs.websocketpp.org/
md_tutorials_utility_server_utility_server.html)翻译
本教程将逐步讨论构建基本的WebSocket++服务器。本教程的最终产品是示例部分中的utility_server示例应用程序。该服务器演示以下功能:
本教程是该库的0.6.x版本的最新版本。
第1步:添加WebSocket++包括并设置服务器端点类型
WebSocket++包括两种主要的对象类型:端点和连接。
端点创建并启动新的连接,并维护这些连接的默认设置。端点还管理任何共享的网络资源
一旦启动了连接,端点和连接之间就不存在链接。端点将所有默认设置复制到新连接中。更改端点上的默认设置只会影响未来的连接。
连接存储特定于每个WebSocket会话的信息。
连接不维护指向其关联端点的链接。
端点不维护未完成连接的列表。如果你的应用程序需要便利所有的连接,则需要维护它们自身的列表
WebSocket++端点是通过结合端点角色和端点配置来构建的。
有两种不同类型的端点角色:
websocketpp::server
, 需要用到头文件
术语:端点配置
config template
参数进行配置struct
类型的,它包含了用于生成具有特定属性的断点的类型和静态常量endpoint角色接受一个名为config的模板参数,该参数用于在编译时配置endpoint的行为。
本教程我们将使用一个默认配置,
asio
的库提供, 使用到了
.将配置与端点角色结合起来生成一个完全配置的端点。这种类型将经常被使用,所以建议在这里使用typedef。
typedef websocketpp::server<websocketpp::config::asio> server
utility_server
构造函数
此端点类型将是跟踪服务器状态的实用工具服务器对象的基础。在utility_server
构造函数中,会发生以下几种情况
m_endpoint.set_error_channels(websocketpp::log::elevel::all);
m_endpoint.set_access_channels(websocketpp::log::alevel::all ^ websocketpp::log::alevel::frame_payload);
m_endpoint.init_asio();
utility_server :: run
方法
除了构造函数之外,我们还添加了一个run方法,该方法设置侦听套接字,开始接受连接,启动Asio io_service事件循环。
//监听端口9002
m_endpoint.listen(9002);
//排队连接接受操作
m_endpoint.start_accept();
//启动Asio io_service运行循环
m_endpoint.run();
最后一行m_endpoint.run()
; 将阻塞,直到指示端点停止侦听新链接为止。在运行时,它将侦听和处理新的连接,并接受和处理现有连接的新数据和控制消息。websocket++在异步模式下使用Asio,在这种模式下,可以在单个线程内同时有效的服务多个连接
目前为止,代码是这样的:
//要使用独立版本的Asio,必须定义ASIO_STANDALONE。
//如果使用的是Boost Asio,则将其删除。
//#define ASIO_STANDALONE
#include
#include
#include
typedef websocketpp::server<websocketpp::config::asio> server;
class utility_server {
public:
utility_server() {
//设置日志记录设置
m_endpoint.set_error_channels(websocketpp::log::elevel::all);
m_endpoint.set_access_channels(websocketpp::log::alevel::all ^ websocketpp::log::alevel::frame_payload);
//初始化Asio
m_endpoint.init_asio();
}
void run() {
//监听端口9002
m_endpoint.listen(9002);
//排队连接接受操作
m_endpoint.start_accept();
//启动Asio io_service运行循环
m_endpoint.run();
}
private:
server m_endpoint;
};
int main(){
utility_server s;
s.run();
}
cmakelist.txt是这样写的:
cmake_minimum_required(VERSION 3.16)
project(boosttest)
set(CMAKE_CXX_STANDARD 14)
set(SOURCE_FILES main.cpp)
#add_executable(myboost ${SOURCE_FILES})
set(BOOST_ROOT "/usr/local/boost")
#添加头文件搜索路径
include_directories(/usr/local/boost/include)
#添加库文件搜索路径
link_directories(/usr/local/boost/lib)
#用于将当前目录下的所有源文件的名字保存在变量 DIR_SRCS 中
aux_source_directory(. DIR_SRCS)
add_executable(boosttest ${DIR_SRCS} )
#在这里根据名字boost_thread去寻找libboost_thread.a文件
target_link_libraries(boosttest -lpthread boost_system )
测试:
1、浏览器连接:
效果:
结论:当前必须是websocket连接
2、在线测试工具测试: http://coolaf.com/tool/chattest
效果:
结论:
当前已经完成了ws连接,但是还不能接收客户端发来的消息
第2步:设置消息处理程序以将所有答复回显给原始用户
#include
#include
#include
typedef websocketpp::server<websocketpp::config::asio> server;
class utility_server {
public:
utility_server() {
//设置日志记录设置
m_endpoint.set_error_channels(websocketpp::log::elevel::all);
m_endpoint.set_access_channels(websocketpp::log::alevel::all ^ websocketpp::log::alevel::frame_payload);
m_endpoint.init_asio();
// Set the default open handler to the echo handler
m_endpoint.set_open_handler( bind(&utility_server::open_handler, this, std::placeholders::_1 ));
// Set the default message handler to the echo handler
m_endpoint.set_message_handler(bind(&utility_server::message_handler, this,std::placeholders::_1, std::placeholders::_2));
}
void open_handler(websocketpp::connection_hdl hdl) {
server::connection_ptr con = m_endpoint.get_con_from_hdl( hdl ); // 根据连接句柄获得连接对象
std::string path = con->get_resource(); // 获得URL路径
m_endpoint.get_alog().write( websocketpp::log::alevel::app, "Connected to path " + path );
}
void message_handler(websocketpp::connection_hdl hdl, server::message_ptr msg) {
m_endpoint.send(hdl, msg->get_payload(), msg->get_opcode());
}
void run(int port){
m_endpoint.set_reuse_addr(true);
m_endpoint.listen(port);
m_endpoint.start_accept();
m_endpoint.run();
}
private:
server m_endpoint;
};
int main(){
utility_server s;
s.run(9002);
}
#include
#include
#include
typedef websocketpp::server<websocketpp::config::asio> server;
typedef websocketpp::config::asio::message_type::ptr message_ptr;
using websocketpp::lib::placeholders::_1;
using websocketpp::lib::placeholders::_2;
using websocketpp::lib::bind;
void on_open( server *s, websocketpp::connection_hdl hdl ) {
// 根据连接句柄获得连接对象
server::connection_ptr con = s->get_con_from_hdl( hdl );
// 获得URL路径
std::string path = con->get_resource();
s->get_alog().write( websocketpp::log::alevel::app, "Connected to path " + path );
}
void on_message( server *s, websocketpp::connection_hdl hdl, message_ptr msg ) {
s->send( hdl, msg->get_payload(), websocketpp::frame::opcode::text );
}
int main() {
server echo_server;
// 调整日志策略
echo_server.set_access_channels( websocketpp::log::alevel::all );
echo_server.clear_access_channels( websocketpp::log::alevel::frame_payload );
try {
echo_server.init_asio();
echo_server.set_open_handler( bind( &on_open, &echo_server, ::_1 ));
echo_server.set_message_handler( bind( &on_message, &echo_server, ::_1, ::_2 ));
// 在所有网络接口的9002上监听
echo_server.listen( 9002);
// 启动服务器端Accept事件循环
echo_server.start_accept();
// 启动事件循环(ASIO的io_service),当前线程阻塞
echo_server.run();
} catch ( websocketpp::exception const &e ) {
std::cout << e.what() << std::endl;
} catch ( ... ) {
std::cout << "other exception" << std::endl;
}
}
#include
#include
#include
typedef websocketpp::server<websocketpp::config::asio> server;
void on_message(websocketpp::connection_hdl, server::message_ptr msg) {
std::cout << msg->get_payload() << std::endl;
}
int main() {
server print_server;
print_server.set_message_handler(&on_message);
print_server.set_access_channels(websocketpp::log::alevel::all);
print_server.set_error_channels(websocketpp::log::elevel::all);
print_server.init_asio();
print_server.listen(9002);
print_server.start_accept();
print_server.run();
}
效果:将发送的消息直接打印出来
#include
#include
#include
typedef websocketpp::server<websocketpp::config::asio> server;
using websocketpp::connection_hdl;
using websocketpp::lib::placeholders::_1;
using websocketpp::lib::placeholders::_2;
using websocketpp::lib::bind;
class broadcast_server{
public:
broadcast_server(){
m_server.init_asio();
m_server.set_open_handler(bind(&broadcast_server::on_open,this,::_1));
m_server.set_close_handler(bind(&broadcast_server::on_close,this,::_1));
m_server.set_message_handler(bind(&broadcast_server::on_message,this,::_1,::_2));
}
void on_open(connection_hdl hdl) {
m_connections.insert(hdl);
}
void on_close(connection_hdl hdl) {
m_connections.erase(hdl);
}
void on_message(connection_hdl hdl, server::message_ptr msg) {
for (auto it : m_connections) {
m_server.send(it,msg);
}
}
void run(uint16_t port) {
m_server.listen(port);
m_server.start_accept();
m_server.run();
}
private:
typedef std::set<connection_hdl,std::owner_less<connection_hdl>> con_list;
server m_server;
con_list m_connections;
};
int main(){
broadcast_server server;
server.run(9002);
}
测试:
1、使用在线测试工具: http://coolaf.com/tool/chattest开两个连接
2、在其中的某一个客户端上发送一条消息
效果:可以看到另外一个服务器上被广播了消息
#include
#include
#include
#include
#include
#include
typedef websocketpp::server<websocketpp::config::asio> server;
using websocketpp::connection_hdl;
using websocketpp::lib::placeholders::_1;
using websocketpp::lib::placeholders::_2;
class count_server{
public:
count_server() : m_count(0){
m_server.init_asio();
m_server.set_open_handler(bind(&count_server::on_open,this,_1));
m_server.set_close_handler(bind(&count_server::on_close,this,_1));
}
void on_open(connection_hdl hdl) {
std::lock_guard<std::mutex> lock(m_mutex);
m_connections.insert(hdl);
}
void on_close(connection_hdl hdl) {
std::lock_guard<std::mutex> lock(m_mutex);
m_connections.erase(hdl);
}
void count(){
while (1){
sleep(1);
m_count++;
std::stringstream ss;
ss << m_count;
std::lock_guard<std::mutex> lock(m_mutex);
for (auto it : m_connections) {
m_server.send(it,ss.str(),websocketpp::frame::opcode::text);
}
}
}
void run(uint16_t port) {
m_server.listen(port);
m_server.start_accept();
m_server.run();
}
private:
typedef std::set<connection_hdl,std::owner_less<connection_hdl>> con_list;
int m_count;
server m_server;
con_list m_connections;
std::mutex m_mutex;
};
int main(){
count_server server;
std::thread t(std::bind(&count_server::count,&server));
server.run(9002);
}
效果:当客户端连上来之后,服务器会 客户端发送消息,美中不足的是,发送的消息一模一样
https://docs.websocketpp.org/simple__count__server__thread_8cpp_source.html
## Utility Client
第一步:基础代码
功能:提示用户输入命令,然后对其进行处理
#include
#include
int main() {
bool done = false;
std::string input;
while (!done) {
std::cout << "Enter Command: ";
std::getline(std::cin, input);
if (input == "quit") {
done = true;
} else if (input == "help") {
std::cout
<< "\nCommand List:\n"
<< "help: Display this help text\n"
<< "quit: Exit the program\n"
<< std::endl;
} else {
std::cout << "Unrecognized Command" << std::endl;
}
}
return 0;
}
第2步:添加websocket++并设置一个endpoint type
websocket++包括两种主要的对象类型。端点和连接
WebSocket++端点是通过结合端点角色和端点配置来构建的。
有两种不同类型的端点角色:
websocketpp::client
,需要用到头文件
websocketpp::server
, 需要用到头文件
此处我们是客户端角色
术语:端点配置
config template
参数进行配置struct
类型的,它包含了用于生成具有特定属性的断点的类型和静态常量endpoint角色接受一个名为config的模板参数,该参数用于在编译时配置endpoint的行为。
本教程我们将使用一个默认配置,
asio_client
的库提供, 使用到了
.将配置与端点角色结合起来生成一个完全配置的端点。这种类型将经常被使用,所以建议在这里使用typedef。
typedef websocketpp::client<websocketpp::config::asio_client> client
至此,我们的代码为:
#include
#include
#include
#include
typedef websocketpp::client<websocketpp::config::asio_client> client;
int main() {
bool done = false;
std::string input;
while (!done) {
std::cout << "Enter Command: ";
std::getline(std::cin, input);
if (input == "quit") {
done = true;
} else if (input == "help") {
std::cout
<< "\nCommand List:\n"
<< "help: Display this help text\n"
<< "quit: Exit the program\n"
<< std::endl;
} else {
std::cout << "Unrecognized Command" << std::endl;
}
}
return 0;
}
第3步:创建端点包装器对象(endpoint wrapper object),用来处理初始化和设置后台线程
为了使网络能够即使在后台运行也能够处理用户输入,我们将为WebScoket++处理循环使用一个单独的线程,从而可以令主线程可以自由的处理前台用户的输入。
为了为线程和端点启用简单的RAII风格的资源管理,我们将使用一个包装器对象,在其构造函数中配置它们
有关于: websocketpp :: lib命名空间
本教程使用websocketpp::lib包装器,因为它不知道阅读器的构建环境是什么。对于您的应用程序,除非您对类似的可移植性感兴趣,否则可以直接直接使用这些类型的boost或std版本。
在websocket_endpoint构造函数中,发生了几件事:
m_endpoint.clear_access_channels(websocketpp::log::alevel::all);
m_endpoint.clear_error_channels(websocketpp::log::elevel::all);
m_endpoint.init_asio();
m_endpoint.start_perpetual();
m_thread.reset(new websocketpp::lib::thread(&client::run, &m_endpoint));
目前位置,代码如下:
#include
#include
#include
#include
#include
#include
typedef websocketpp::client<websocketpp::config::asio_client> client;
class websocket_endpoint {
public:
websocket_endpoint () {
m_endpoint.clear_access_channels(websocketpp::log::alevel::all);
m_endpoint.clear_error_channels(websocketpp::log::elevel::all);
m_endpoint.init_asio();
m_endpoint.start_perpetual();
m_thread.reset(new websocketpp::lib::thread(&client::run, &m_endpoint));
}
private:
client m_endpoint;
websocketpp::lib::shared_ptr<websocketpp::lib::thread> m_thread;
};
int main() {
bool done = false;
std::string input;
websocket_endpoint endpoint;
while (!done) {
std::cout << "Enter Command: ";
std::getline(std::cin, input);
if (input == "quit") {
done = true;
} else if (input == "help") {
std::cout
<< "\nCommand List:\n"
<< "help: Display this help text\n"
<< "quit: Exit the program\n"
<< std::endl;
} else {
std::cout << "Unrecognized Command" << std::endl;
}
}
return 0;
}
第4步:打开socket连接
这一步向utility_client添加了两个新命令
新的连接元数据对象:
connection_metadata
定义了一个对象更新 websocket_endpoint :
websocket_endpoint
对象已经获得一些新的数据成员和方法。现在它跟踪连接ID及其关联的元数据之间的映射以及要分发的下一个顺序ID号。
连接方法: 新的WebSocket连接通过三步过程启动
connection_ptr: websocket++使用引用计数的共享指针来跟踪与连接有关的资源。该指针的类型为endpoint::connection_ptr
connection_prt
除非在以下特定情况下,一般都是不安全的
endpoint::get_connection(...)
和endpoint::connect(): get_connection
返回的connection_ptr。使用这个指针配置新连接是安全的。一旦您提交了要连接的连接,您可能就不再使用connection_ptr,并且应该立即丢弃它,以获得最佳的内存管理。connection_hdl: 由于connection_ptr库的线程安全性有限,该库还提供了更灵活的连接标识符,connection_hdl
websocketpp::connection_hdl
,在
中定义
connection_hdl
是线程安全的,可以随时从任何线程使用。可以将它们复制并存储在容器中。删除hdl不会以任何方式影响连接。如果连接创建成功,将生成下一个连续的连接ID,并将connection_metadata对象插入到该ID下的连接列表中。
int new_id = m_next_id++;
metadata_ptr metadata(new connection_metadata(new_id, con->get_handle(), uri));
m_connection_list[new_id] = metadata;
注册处理程序:
utility_client注册一个打开和失败处理程序。我们将使用它们来跟踪每个连接是成功打开还是失败。如果成功打开,我们将从打开握手中收集一些信息,并将其与我们的连接元数据一起存储。
在此示例中,我们将设置特定于连接的处理程序,这些处理程序直接绑定到与我们的连接关联的元数据对象。这使我们可以避免在每个处理程序中执行查找来查找我们计划更新的元数据对象,这效率更高一些。
让我们详细查看要发送的绑定参数:
con->set_open_handler(websocketpp::lib::bind(
&connection_metadata::on_open,
metadata,
&m_endpoint,
websocketpp::lib::placeholders::_1
));
connection_metadata
的on_open成员函数的地址。最后,我们调用已endpoint::connect()配置的连接请求并返回新的连接ID。
新命令
} else if (input.substr(0,7) == "connect") {
int id = endpoint.connect(input.substr(8));
if (id != -1) {
std::cout << "> Created connection with id " << id << std::endl;
}
} else if (input.substr(0,4) == "show") {
int id = atoi(input.substr(5).c_str());
connection_metadata::ptr metadata = endpoint.get_metadata(id);
if (metadata) {
std::cout << *metadata << std::endl;
} else {
std::cout << "> Unknown connection id " << id << std::endl;
}
}