[C++项目组件]cpp-httplib与 websocketpp的简单介绍和使用

websocketpp的简单介绍和使用

一.cpp-httplib

1.cpp-httplib的介绍

实体信息:C++ HTTP 库(cpp-httplib),它是轻量级的 C++ HTTP 客户端/服务器库,具有以下主要特点:

  • 轻量级:仅需一个头文件,不依赖任何外部库;
  • 跨平台:支持 Windows、Linux、macOS 等操作系统;
  • 支持同步和异步操作:开发者可按需选择操作方式;
  • 支持 HTTP/1.1:实现 HTTP/1.1 协议,包含持久连接、管道化等特性;
  • 支持 multipart/form-data:便于文件上传场景;
  • 支持 SSL/TLS:通过 OpenSSL 或 mbedTLS 库,支持 HTTPS 和 WSS;
  • 简单易用:API 设计简洁,易学习使用;
  • 性能良好:轻量级但性能表现出色,适配多种应用场景;
  • 社区活跃:社区持续更新,不断加入新功能与改进。

2.接口

cpp 复制代码
namespace httplib {
struct Request {
    std::string method;
    std::string path;

    Headers headers;
    std::string body;
    Params params;
};
struct Response {
    std::string version;
    int status = -1;
    std::string reason;
    Headers headers;
    std::string body;
    void set_content(const std::string &s, const std::string &content_type);
    void set_header(const std::string &key, const std::string &val);
};
class Server {
    using Handler = std::function<void(const Request &, Response &)>;
    Server &Get(const std::string &pattern, Handler handler);
    Server &Post(const std::string &pattern, Handler handler);
    Server &Put(const std::string &pattern, Handler handler);
    Server &Delete(const std::string &pattern, Handler handler);
    bool listen(const std::string &host, int port);
};
class Client {
    explicit Client(const std::string &host, int port);
    Result Get(const std::string &path, const Headers &headers);
    Result Post(const std::string &path, const std::string &body, const std::string &content_type);
    Result Put(const std::string &path, const std::string &body, const std::string &content_type);
    Result Delete(const std::string &path, const std::string &body, const std::string &content_type);
};
}

3.使用样例

cpp 复制代码
#include "../common/httplib.h"
#include <iostream>
using namespace std;

int main()
{
    // 1.实例化服务器对象
    httplib::Server server;
    // 2.注册回调函数 void(const httplib::Request &, httplib::Response &);
    server.Get("/hi",[](const httplib::Request &req, httplib::Response &rsp){
        cout<<req.method<< endl;
        cout<<req.path<< endl;
        for(auto it :req.headers)
        {
            cout<<it.first<<": "<<it.second<<endl; 
        }
        rsp.status=200;
        string body="<html><body><h1>Hello yjt</h1><body></html>";
        rsp.set_content(body,"test/html");
        rsp.status=200;
    });
    // 3.启动服务器
    server.listen("0.0.0.0",9090);
    return 0;
}

二.websocketpp

1.websocketpp介绍

WebSocketpp 是一个跨平台的开源(BSD 许可证)头部专用 C++ 库,它实现了 RFC6455(WebSocket 协议)和 RFC7692(WebSocketCompression Extensions)。它允许将 WebSocket 客户端和服务器功能集成到 C++ 程序中。在最常见的配置中,全功能网络 I/O 由 Asio 网络库提供。

WebSocketpp 的主要特性

  • 事件驱动的接口
  • 支持 HTTP/HTTPS、WS/WSS、IPv6
  • 灵活的依赖管理 --- Boost 库/C++11 标准库
  • 可移植性:Posix/Windows、32/64bit、Intel/ARM
  • 线程安全

WebSocketpp 同时支持 HTTP 和 WebSocket 两种网络协议,比较适用于我们本次的项目,所以我们选用该库作为项目的依赖库用来搭建 HTTP 和 WebSocket 服务器。

2. WebSocketpp常用接口

cpp 复制代码
namespace websocketpp
{
    typedef lib::weak_ptr<void> connection_hdl;
    template <typename config>
    class endpoint : public config::socket_type
    {
        typedef lib::shared_ptr<lib::asio::steady_timer> timer_ptr;
        typedef typename connection_type::ptr connection_ptr;
        typedef typename connection_type::message_ptr message_ptr;
        typedef lib::function<void(connection_hdl)> open_handler;
        typedef lib::function<void(connection_hdl)> close_handler;
        typedef lib::function<void(connection_hdl)> http_handler;
        typedef lib::function<void(connection_hdl, message_ptr)> message_handler;

        /* websocketpp::log::alevel::none 禁止打印所有日志*/
        void set_access_channels(log::level channels);   /*设置日志打印等级*/
        void clear_access_channels(log::level channels); /*清除指定等级的日志*/
        /*设置指定事件的回调函数*/
        void set_open_handler(open_handler h);       /*websocket 握手成功回调处理函数*/
        void set_close_handler(close_handler h);     /*websocket 连接关闭回调处理函数*/
        void set_message_handler(message_handler h); /*websocket 消息回调处理函数*/
        void set_http_handler(http_handler h);       /*http 请求回调处理函数*/
        /*发送数据接口*/
        void send(connection_hdl hdl, std::string &payload, frame::opcode::value op);
        void send(connection_hdl hdl, void *payload, size_t len, frame::opcode::value op);
        /*关闭连接接口*/
        void close(connection_hdl hdl, close::status::value code, std::string &reason);
        /*获取 connection_hdl 对应连接的 connection_ptr*/
        connection_ptr get_con_from_hdl(connection_hdl hdl);
        /*websocketpp 基于 asio 框架实现, init_asio 用于初始化 asio 框架中的 io_service 调度器*/
        void init_asio();
        /*设置是否启用地址重用*/
        void set_reuse_addr(bool value);
        /*设置 endpoint 的绑定监听端口*/
        void listen(uint16_t port);
        /*对 io_service 对象的 run 接口封装,用于启动服务器*/
        std::size_t run();
        /*websocketpp 提供的定时器,以毫秒为单位*/
        timer_ptr set_timer(long duration, timer_handler callback);
    };
	
	// 继承自 endpoint
    template <typename config>
    class server : public endpoint<connection<config>, config>
    {
        /*初始化并启动服务端监听连接的 accept 事件处理*/
        void start_accept();
    }; 
    

	template <typename config>
    class connection
        : public config::transport_type::transport_con_type,
          public config::connection_base
    {
        /*发送数据接口*/
        error_code send(std::string &payload, frame::opcode::value op = frame::opcode::text);
        /*获取 http 请求头部*/
        std::string const &get_request_header(std::string const &key)
        /*获取请求正文*/
        std::string const &get_request_body();
        /*设置响应状态码*/
        void set_status(http::status_code::value code);
        /*设置 http 响应正文*/
        void set_body(std::string const &value);
        /*添加 http 响应头部字段*/
        void append_header(std::string const &key, std::string const &val);
        /*获取 http 请求对象*/
        request_type const &get_request();
        /*获取 connection_ptr 对应的 connection_hdl */
        connection_hdl get_handle();
    };
    
    namespace http
    {
        namespace parser
        {
            class parser
            {
                std::string const &get_header(std::string const &key)
                std::string const &get_body() typedef std::map<std::string, std::string, utility::ci_less> header_list;
                header_list const &get_headers();
            }

            class request : public parser
            {
                /*获取请求方法*/
                std::string const &get_method();
                /*获取请求 uri 接口*/
                std::string const &get_uri();
            };
        }
    };
    
    // 消息缓冲区类
    namespace message_buffer
    {
        /*获取 websocket 请求中的 payload 数据类型*/
        frame::opcode::value get_opcode();
        /*获取 websocket 中 payload 数据*/
        std::string const &get_payload();
    };
    
    namespace log
    {
        struct alevel
        {
            static level const none = 0x0;
            static level const connect = 0x1;
            static level const disconnect = 0x2;
            static level const control = 0x4;
            static level const frame_header = 0x8;
            static level const frame_payload = 0x10;
            static level const message_header = 0x20;
            static level const message_payload = 0x40;
            static level const endpoint = 0x80;
            static level const debug_handshake = 0x100;
            static level const debug_close = 0x200;
            static level const devel = 0x400;
            static level const app = 0x800;
            static level const http = 0x1000;
            static level const fail = 0x2000;
            static level const access_core = 0x00003003;
            static level const all = 0xffffffff;
        };
    }
    
    namespace http
    {
        namespace status_code
        {
            enum value
            {
                uninitialized = 0,
                continue_code = 100,
                switching_protocols = 101,
                ok = 200,
                created = 201,
                accepted = 202,
                non_authoritative_information = 203,
                no_content = 204,
                reset_content = 205,
                partial_content = 206,
                multiple_choices = 300,
                moved_permanently = 301,
                found = 302,
                see_other = 303,
                not_modified = 304,
                use_proxy = 305,
                temporary_redirect = 307,
                bad_request = 400,
                unauthorized = 401,
                payment_required = 402,
                forbidden = 403,
                not_found = 404,
                method_not_allowed = 405,
                not_acceptable = 406,
                proxy_authentication_required = 407,
                request_timeout = 408,
                conflict = 409,
                gone = 410,
                length_required = 411,
                precondition_failed = 412,
                request_entity_too_large = 413,
                request_uri_too_long = 414,
                unsupported_media_type = 415,
                request_range_not_satisfiable = 416,
                expectation_failed = 417,
                im_a_teapot = 418,
                upgrade_required = 426,
                precondition_required = 428,
                too_many_requests = 429,
                request_header_fields_too_large = 431,
                internal_server_error = 500,
                not_implemented = 501,
                bad_gateway = 502,
                service_unavailable = 503,
                gateway_timeout = 504,
                http_version_not_supported = 505,
                not_extended = 510,
                network_authentication_required = 511
            };
        }
    }
    
    namespace frame
    {
        namespace opcode
        {
            enum value
            {
                continuation = 0x0,
                text = 0x1,
                binary = 0x2,
                rsv3 = 0x3,
                rsv4 = 0x4,
                rsv5 = 0x5,
                rsv6 = 0x6,
                rsv7 = 0x7,
                close = 0x8,
                ping = 0x9,
                pong = 0xA,
                control_rsvb = 0xB,
                control_rsvc = 0xC,
                control_rsvd = 0xD,
                control_rsve = 0xE,
                control_rsvf = 0xF,
            };
        }
    }
}

3.使用样例

websocket 服务器

使用 Websocketpp 实现一个简单的 http 和 websocket 服务器

cpp 复制代码
#include <websocketpp/config/asio_no_tls.hpp>
#include <websocketpp/server.hpp>
#include <iostream>

// 0. 定义 server 类型
typedef websocketpp::server<websocketpp::config::asio> server_t;

void onOpen(websocketpp::connection_hdl hdl) {
    std::cout << "websocet 长连接建立成功!" << std::endl;
}
void onClose(websocketpp::connection_hdl hdl) {
    std::cout << "websocet 长连接断开!" << std::endl;
}

void onMessage(server_t *server, websocketpp::connection_hdl hdl, server_t::message_ptr msg) {
    // 1. 获取有效消息载荷数据,进行业务处理
    std::string body = msg->get_payload();
    std::cout << "收到消息: " << body << std::endl;
    // 2. 对客户端进行响应
    // 获取通信连接
    auto conn = server->get_con_from_hdl(hdl);  
    // 发送数据
    conn->send(body + "-HELLO!", websocketpp::frame::opcode::value::text);
}

int main()
{
    // 1. 实例化服务器对象
    server_t server;
    // 2. 初始化日志输出框架 --- 关闭日志输出
    server.set_access_channels(websocketpp::log::alevel::none);
    // 3. 初始化 asio 框架
    server.init_asio();
    // 4. 设置消息处理/连接握手成功/连接关闭回调函数
    server.set_open_handler(onOpen);
    server.set_close_handler(onClose);
    auto msg_handler = std::bind(onMessage, &server, std::placeholders::_1, std::placeholders::_2);
    server.set_message_handler(msg_handler);
    // 5. 启用地址重用
    server.set_reuse_addr(true);
    // 6. 设置监听端口
    server.listen(9090);
    // 7. 开始监听
    server.start_accept();
    // 8. 启动服务器
    server.run();

    return 0;
}

HTTP 客户端

使用浏览器作为 http 客户端即可, 访问服务器的 8888 端口

cpp 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,
initial-scale=1.0">
    <title>Test Websocket</title>
</head>

<body>
    <input type="text" id="message">
    <button id="submit">提交</button>
    <script>
        // 创建 websocket 实例
        // ws://192.168.51.100:8888
        // 类比 http
        // ws 表示 websocket 协议
        // 192.168.51.100 表示服务器地址
        // 8888 表示服务器绑定的端口
        let websocket = new WebSocket("ws://111.230.94.31:9090");
        // 处理连接打开的回调函数
        websocket.onopen = function () {
            console.log("连接建立");
        }
        // 处理收到消息的回调函数
        // 控制台打印消息
        websocket.onmessage = function (e) {
            console.log("收到消息: " + e.data);
        }
        // 处理连接异常的回调函数
        websocket.onerror = function () {
            console.log("连接异常");
        }
        // 处理连接关闭的回调函数
        websocket.onclose = function () {
            console.log("连接关闭");
        }
        // 实现点击按钮后, 通过 websocket 实例 向服务器发送请求
        let input = document.querySelector('#message');
        let button = document.querySelector('#submit');
        button.onclick = function () {
            console.log("发送消息: " + input.value);
            websocket.send(input.value);
        }
    </script>
</body>

</html>

在控制台中我们可以看到连接建立、客户端和服务器通信以及断开连接的过程(关闭服务器就会看到断开连接的现象)

相关推荐
什么半岛铁盒3 小时前
C++项目:仿muduo库高并发服务器---------LoopThreadPool模块和TcpServer模块的实现
linux·服务器·c++·mysql·ubuntu
GISer_Jing3 小时前
0926第一个口头OC——快手主站前端
开发语言·前端·javascript
风语者6663 小时前
perl踩坑系列===正则表达式第2坑---split中的“或”操作符
开发语言·正则表达式·perl
狂团商城小师妹3 小时前
JAVA露营基地预约户外露营预约下单系统小程序
java·开发语言·微信小程序·小程序
lingran__3 小时前
速通ACM省铜第十七天 赋源码(Racing)
c++·算法
大飞pkz4 小时前
【设计模式】策略模式
开发语言·设计模式·c#·策略模式
"菠萝"4 小时前
C#知识学习-015(修饰符_4)
开发语言·windows·c#
无名指的等待7125 小时前
Redisson的Lock和TryLock的区别
java·开发语言·数据库
yanqiaofanhua5 小时前
C语言自学--自定义类型:结构体
c语言·开发语言·算法