WebSocketpp

盛承
2023-12-01

git 源码 https://github.com/zaphoyd/websocketpp/tree/1b11fd301531e6df35a6107c1e8665b1e77a2d8e

WebSocketpp 简介

WebSocket是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。WebSocket 使得 CS 之间的数据交换变得更加简单,允许 S 主动向 C 推送数据。WebSocket 和 HTTP 一样都是应用层的一种协议,可以使用各种编程语言实现之。

WebSocketpp 是一个仅仅由头文件构成的 C++库,它实现了 WebSocket 协议(rfc 6455),可以在项目中方便简洁地实现客户端和服务器。WebSocketpp 底层使用了 C++ 的 iostream 库和基于 Boost 的 Asio 库。

下载安装

如前所述,WebSocketpp 基于 Boost 的 Asio 库,并且如果要在项目中实现wss,则还需要 libssl-dev 库的支持。

安装 Boost 库。
安装 libssl-dev 库。
安装 WebSocketpp 库。
去官网下载源码,WebSocketpp
创建build目录,用来存放cmake生成的中间文件。
cd build && cmake ..,执行MakeList.txt。
make && sudo make install,安装 WebSocket 库,默认放在/usr/local/include中。
当前,WebSocketpp 0.8.1 最高兼容的 Boost 版本是 1.69.0,WebSocketpp 的兼容性问题

编译执行样例

# 编译服务器
cd websocketpp/examples/echo_server
g++ echo_server.cpp -o echo_server -lboost_system -lpthread
./echo_server

# 编译客户端
cd websocketpp/examples/echo_client
g++ echo_client.cpp -o echo_client -lboost_system -lpthread
./echo_client

WebSocket 基本概念

如果要在项目中利用 WebSocketpp 实现 WebSocket 客户端或服务器,基本要include两种头文件,一种是决定编程角色的头文件(Role),另一种是配置编程角色的配置头文件(Config)。

Role:
Role 有两种,分别是 Server 和 Client,很好理解。

如果开发的是客户端(Client),则要include <websocketpp/client.hpp>,后面就可以使用websocketpp::client<>模板类来创建不同配置的客户端。
如果开发的是服务器(Server),则要include <websocketpp/server.hpp>,后面就可以使用websocketpp::server<>模板类来创建不同配置的服务器。

Config
Config 主要用来配置 client 或者 server 的属性,WebSocketpp 主要提供了 3 种配置:

config::core,有限功能的配置,基于 C++11 的功能,不需要 Boost 库支持。
config::asio,使用 Boost 的 Asio 库,提供完整的 ws 功能。
config::asio_tls,使用 Boost 的 Asio 库,提供完整的 wss 功能。
这样,根据不同的 Role 和 Config 组合,可以实现各种各类的 Client 和 Server,如下:

Server_config    Server    Client    Client_config
core    core.hpp    core_client.hpp    core_client
asio    asio_no_tls.hpp    asio_no_tls_client.hpp    asio_client
asio_tls    asio.hpp    asio_client.hpp    asio_tls_client
如需要开发一个基于 Boost Asio,使用 ws 的服务器的话,要包含的头文件如下:

#include <websocketpp/server.hpp>
#include <websocketpp/config/asio_no_tls.hpp>

EndPoint
EndPoint 看成是最后需要实现的具体的物件,即哪种配置的服务器或者客户端。

如需要开发一个基于 Boost Asio,使用 ws 的服务器的话,完整的开头应该如下:

#include <websocketpp/server.hpp>
#include <websocketpp/config/asio_no_tls.hpp>

typedef websocketpp::server<websocketpp::config::asio> server;

WebSocketpp 框架的基本步骤

相关基础介绍

websocketpp::connection_hdl,用来识别当前连接用的,可以通过endpoint.get_con_formhdl获取连接信息。
websocketpp::server<>::message_ptr,保存了收到的数据,msg.get_payload()返回的是具体的数据,get_opcode()返回的是数据的编码格式。
endpoint.send(hdl, data_str, data_code),用于发送数据,需要指明发送的内容和编码格式。
主动关闭连接:conn->close(cvValue, strReason)
绑定的事件列表,详细可查看事件列表
open([endpint *,] connection_hdl);
fail([endpint *,] connection_hdl);
close([endpint *,] connection_hdl);
message([endpint *,] connection_hdl, message_ptr);
tls_context_ptr tls_init(connection_hdl);

endpoint.set_open_handler(std::bind(&on_open, &endpoint, ::_1 ));
endpoint.set_fail_handler(std::bind(&on_fail, &endpoint, ::_1 ));
endpoint.set_message_handler(std::bind(&on_message, &endpoint, ::_1, ::_2 ));
endpoint.set_close_handler(std::bind(&on_close, &endpoint, ::_1 ));

Client
创建一个 client。
初始化 client。
init_asio(),初始化内部的 Boost 的 Asio,作为后续连接使用。
start_perpetual(),设置 client 为永久执行,这样就可以多次连接,否则只能进行一次连接,非必须.
绑定事件回调函数,主要有onopen(), onclose(), onfail(), onmessage()等。
建立连接。
get_connection(),获取连接。
connect(),开始连接。
开始执行主循环。

#include <websocketpp/config/asio_no_tls_client.hpp>
#include <websocketpp/client.hpp>

#include <iostream>

using websocketpp::lib::bind;
using websocketpp::lib::placeholders::_1;
using websocketpp::lib::placeholders::_2;

typedef websocketpp::client<websocketpp::config::asio_client> client;
typedef websocketpp::config::asio_client::message_type::ptr message_ptr;
// 消息回调函数
void on_message(client *c, websocketpp::connection_hdl hdl, message_ptr msg)
{
    std::cout << "on_message called with hdl: " << hdl.lock().get()
              << " and message: " << msg->get_payload()
              << std::endl;
    websocketpp::lib::error_code ec;
    c->send(hdl, msg->get_payload(), msg->get_opcode(), ec);
    if (ec)
    {
        std::cout << "Echo failed because: " << ec.message() << std::endl;
    }
}
int main()
{
    client c; // 创建一个client
    std::string uri = "ws://localhost:9002";
    try
    {
        // 设置日志
        c.set_access_channels(websocketpp::log::alevel::all);
        c.clear_access_channels(websocketpp::log::alevel::frame_payload);
        // 初始化asio
        c.init_asio();
        // 绑定消息回调函数
        c.set_message_handler(bind(&on_message, &c, ::_1, ::_2));
        // 创建连接
        websocketpp::lib::error_code ec;
        client::connection_ptr con = c.get_connection(uri, ec);
        if (ec)
        {
            std::cout << "could not create connection because: " << ec.message() << std::endl;
            return 0;
        }
        // 建立连接
        c.connect(con);
        // 开始主循环
        c.run();
    }
    catch (websocketpp::exception const &e)
    {
        std::cout << e.what() << std::endl;
    }
    return 0;
}

服务器
创建一个 server。
初始化 server。
init_asio(),初始化内部的 Boost 的 Asio,作为后续连接使用。
绑定事件回调函数,主要有onopen(), onclose(), onfail(), onmessage()等。
接受客户端连接,start_accept()
开始执行主循环。

#include <websocketpp/config/asio_no_tls.hpp>
#include <websocketpp/server.hpp>

#include <iostream>

using websocketpp::lib::bind;
using websocketpp::lib::placeholders::_1;
using websocketpp::lib::placeholders::_2;

typedef websocketpp::server<websocketpp::config::asio> server;
typedef server::message_ptr message_ptr;

// 消息回调函数
void on_message(server *s, websocketpp::connection_hdl hdl, message_ptr msg)
{
    std::cout << "on_message called with hdl: " << hdl.lock().get()
              << " and message: " << msg->get_payload()
              << std::endl;

    if (msg->get_payload() == "stop-listening")
    {
        s->stop_listening();
        return;
    }
    try
    {
        s->send(hdl, msg->get_payload(), msg->get_opcode());
    }
    catch (websocketpp::exception const &e)
    {
        std::cout << "Echo failed because: "
                  << "(" << e.what() << ")" << std::endl;
    }
}
int main()
{
    // 创建一个实例
    server echo_server;
    try
    {
        // 设置日志
        echo_server.set_access_channels(websocketpp::log::alevel::all);
        echo_server.clear_access_channels(websocketpp::log::alevel::frame_payload);
        // 初始化Asio
        echo_server.init_asio();
        // 绑定消息回调函数
        echo_server.set_message_handler(bind(&on_message, &echo_server, ::_1, ::_2));
        // 监听9002端口
        echo_server.listen(9002);
        // 开启接受服务
        echo_server.start_accept();
        // 开始主循环
        echo_server.run();
    }
    catch (websocketpp::exception const &e)
    {
        std::cout << e.what() << std::endl;
    }
    catch (...)
    {
        std::cout << "other exception" << std::endl;
    }
}

wss 协议

#include <websocketpp/config/asio_client.hpp>
#include <websocketpp/client.hpp>

#include <iostream>

typedef websocketpp::client<websocketpp::config::asio_tls_client> client;
typedef std::shared_ptr<boost::asio::ssl::context> context_ptr;
using websocketpp::lib::placeholders::_1;
using websocketpp::lib::placeholders::_2;
using websocketpp::lib::bind;

typedef websocketpp::config::asio_client::message_type::ptr message_ptr;

void on_message(client* c, websocketpp::connection_hdl hdl, message_ptr msg) {
    std::cout << "on_message called with hdl: " << hdl.lock().get()
              << " and message: " << msg->get_payload()
              << std::endl;

    websocketpp::lib::error_code ec;

    c->send(hdl, msg->get_payload(), msg->get_opcode(), ec);
    if (ec) {
        std::cout << "Echo failed because: " << ec.message() << std::endl;
    }
}

static context_ptr on_tls_init() {
    // establishes a SSL connection
    context_ptr ctx = std::make_shared<boost::asio::ssl::context>(boost::asio::ssl::context::sslv23);

    try {
        ctx->set_options(boost::asio::ssl::context::default_workarounds |
                         boost::asio::ssl::context::no_sslv2 |
                         boost::asio::ssl::context::no_sslv3 |
                         boost::asio::ssl::context::single_dh_use);
    } catch (std::exception &e) {
        std::cout << "Error in context pointer: " << e.what() << std::endl;
    }
    return ctx;
}

int main(int argc, char* argv[]) {
    client c;
    std::string uri = "wss://localhost";
    c.set_access_channels(websocketpp::log::alevel::all);
    c.clear_access_channels(websocketpp::log::alevel::frame_payload);
    c.init_asio();
    c.set_tls_init_handler(bind(&on_tls_init)); // wss协议
    c.set_message_handler(bind(&on_message,&c,::_1,::_2));
    websocketpp::lib::error_code ec;
    client::connection_ptr con = c.get_connection(uri, ec);
    if (ec) {
        std::cout << "could not create connection because: " << ec.message() << std::endl;
        return 0;
    }
    c.connect(con);
    c.run();
}

 类似资料:

相关阅读

相关文章

相关问答