基于 libwebsockets 实现 websocket 服务

1.定义一个协议数组

cpp 复制代码
/*
struct lws_protocols {
    const char *name;                // 协议名称
    lws_callback_function callback;  // 消息/事件回调函数(核心,处理连接、消息、关闭等)
    size_t per_session_data_size;    // user 的大小
    size_t rx_buffer_size;           // 接收缓冲区大小(建议 ≥ 4096,根据业务调整)
    unsigned int id;                 // 协议ID(内部使用,可设为 0 自动分配)
    void *user;                      // 全局用户数据(可选,所有连接共享)
    size_t tx_packet_size;           // 发送数据包大小(0 表示默认)
};
*/

lws_protocols WebSocketServer::protocols[] = {
    {
        "ws-protocol",                
        WebSocketServer::static_callback,
        sizeof(void*),               // user 只用来存储 this 指针,所以设置为指针大小即可    
        1024 * 1024,
    },
    { NULL, NULL, 0, 0 }
};
  1. 设置参数,并创建 libwebsockets 上下文
cpp 复制代码
    lws_context_creation_info info = {};
    info.port = m_port;
    info.iface = NULL; // 监听所有网络接口
    info.protocols = protocols;
    info.gid = -1;
    info.uid = -1;
    info.user = this;
    
    // SSL 配置
    if (m_use_ssl) {
        info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
        // info.ssl_cert_filepath = "server.crt";
        // info.ssl_private_key_filepath = "server.key";
    }

    // 创建 libwebsockets 上下文
    m_context = lws_create_context(&info);
    if (!m_context) {
        std::cerr << "Failed to create libwebsockets context!" << std::endl;
        return false;
    }
  1. 创建一个循环,调用 lws_service 处理事件
cpp 复制代码
    while (m_running && m_context) {
        // 处理事件,超时时间为50毫秒
        lws_service(m_context, 50);
    }
  1. 回调函数实现,由于回调函数不能是普通成员函数,所以我们需要在静态成员函数中获取 this 指针,然后调用实际的回调
cpp 复制代码
int WebSocketServer::static_callback(lws* wsi, lws_callback_reasons reason, 
                                   void* user, void* in, size_t len) {
    lws_context* context = lws_get_context(wsi);
    WebSocketServer* server = static_cast<WebSocketServer*>(lws_context_user(context));
    if (server) {
        return server->instance_callback(wsi, reason, user, in, len);
    }
    return 0;
}
cpp 复制代码
int WebSocketServer::instance_callback(lws* wsi, lws_callback_reasons reason, 
                                     void* user, void* in, size_t len) {
    switch (reason) {
        case LWS_CALLBACK_ESTABLISHED:
            {
                std::lock_guard<std::shared_mutex> lock(m_shareMutex);
                std::string clientId = getUId();
                m_clients[clientId] = wsi;
                m_msgQueues[wsi] = std::queue<std::string>();
                for (const auto& callback : m_connectedCallbacks) {
                    std::thread t([=]() {
                        callback(clientId);
                    });
                    t.detach();
                }
            }
            break;
            
        case LWS_CALLBACK_RECEIVE:
            handleReceive(wsi, in, len);
            break;
            
        case LWS_CALLBACK_SERVER_WRITEABLE:
            handleWritable(wsi);
            break;
            
        case LWS_CALLBACK_CLOSED:
            {
                std::lock_guard<std::shared_mutex> lock(m_shareMutex);
                for (auto it = m_clients.begin(); it != m_clients.end(); ++it) {
                    if (it->second == wsi) {
                        for (const auto& callback : m_closedCallbacks) {
                            std::thread t([=]() {
                                callback(it->first);
                            });
                            t.detach();
                        }
                        m_clients.erase(it->first);
                        break;
                    }
                }
                m_msgQueues.erase(wsi); // 清理连接对应的发送队列
            }
            break;
            
        default:
            break;
    }
    return 0;
}

这个处理回调函数的地方我用了线程处理,因为我在接收消息回调里调用了发送消息接口,我在消息处理函数和发送消息函数里都加锁了,接收到消息时,发送消息,此时由于接收消息这里加锁了,发送消息函数得不到执行,接收消息回调就无法结束,锁也就无法释放。

  1. 接收消息处理
cpp 复制代码
void WebSocketServer::handleReceive(lws* wsi, void* in, size_t len) {
    if (lws_frame_is_binary(wsi)) {
        // 处理二进制消息
    } else {
        // 处理文本消息
        std::string message((const char*)in, len);
        std::lock_guard<std::shared_mutex> lock(m_shareMutex);
        for (auto it = m_clients.begin(); it != m_clients.end(); ++it) {
            if (it->second == wsi) {
                for (const auto& callback : m_receiveMsgCallback) {
                    std::thread t([=]() {
                        callback(it->first, message);
                    });
                    t.detach();
                }
                break;
            }
        }
    }
}
  1. 发送消息处理
cpp 复制代码
void WebSocketServer::handleWritable(lws* wsi) {
    std::lock_guard<std::shared_mutex> lock(m_shareMutex);
    
    auto it = m_msgQueues.find(wsi);
    if (it == m_msgQueues.end() || it->second.empty()) {
        return;
    }
    std::queue<std::string>& queue = it->second;
    if (!queue.empty()) {
        const std::string& message = queue.front();
        // 准备发送缓冲区(必须预留 LWS_PRE 字节)
        unsigned char buf[LWS_PRE + message.size()];
        memcpy(buf + LWS_PRE, message.c_str(), message.size());
        
        // 发送消息
        int sent = lws_write(wsi, buf + LWS_PRE, message.size(), LWS_WRITE_TEXT);
        if (sent < 0) {
            std::cerr << "Failed to send message!" << std::endl;
        } else {
            //std::cout << "Message sent successfully, length: " << sent << std::endl;
        }
        
        queue.pop(); // 移除已发送的消息
        if (!queue.empty()) {
            lws_callback_on_writable(wsi);
        }
    }
}

完整代码如下

头文件

cpp 复制代码
#ifndef WEBSOCKET_SERVER_H
#define WEBSOCKET_SERVER_H

#include <string>
#include <map>
#include <queue>
#include <mutex>
#include <shared_mutex>
#include <condition_variable>
#include <atomic>
#include <functional>
#include <sstream>
#include <iomanip>
#include "../thirdparty/libwebsockets/include/libwebsockets.h"

struct lws;
struct lws_context;

class WebSocketServer {
public:
    explicit WebSocketServer(int port, bool use_ssl = false);
    virtual ~WebSocketServer();
    bool start();
    void stop();
    void wait();

    void connectedCallback(std::function<void(const std::string&)> callback) { m_connectedCallbacks.emplace_back(callback); }
    void receiveMsgCallback(std::function<void(const std::string&, const std::string&)> callback) { m_receiveMsgCallback.emplace_back(callback); }
    void closedCallback(std::function<void(const std::string&)> callback) { m_closedCallbacks.emplace_back(callback); }
    bool sendMessage(const std::string& client, const std::string& message);

    std::string getUId() {
        int counter = 0;
        std::string id = std::to_string(counter);
        while (m_clients.find(id) != m_clients.end()) {
            ++counter;
            id = std::to_string(counter);
        }
        return id;
    }

private:
    static int static_callback(lws* wsi, lws_callback_reasons reason, 
                              void* user, void* in, size_t len);
    int instance_callback(lws* wsi, lws_callback_reasons reason, 
                         void* user, void* in, size_t len);

    void handleWritable(lws* wsi);
    void handleReceive(lws* wsi, void* in, size_t len);

    int m_port;
    bool m_use_ssl;
    std::atomic<bool> m_running;
    lws_context* m_context;
    
    std::map<std::string, lws*> m_clients;
    std::map<lws*, std::queue<std::string>> m_msgQueues;
    std::shared_mutex m_shareMutex;

    std::vector<std::function<void(const std::string&)>> m_connectedCallbacks;
    std::vector<std::function<void(const std::string&, const std::string&)>> m_receiveMsgCallback;
    std::vector<std::function<void(const std::string&)>> m_closedCallbacks;

    // 协议数组
    static lws_protocols protocols[];
};

#endif // WEBSOCKET_SERVER_H

cpp 文件

cpp 复制代码
#include "WebSocketServer.h"
#include <iostream>
#include <thread>
#include <signal.h>

/*
struct lws_protocols {
    const char *name;                // 协议名称(客户端需通过 Sec-WebSocket-Protocol 指定)
    lws_callback_function callback;  // 消息/事件回调函数(核心,处理连接、消息、关闭等)
    size_t per_session_data_size;    // user 的大小
    size_t rx_buffer_size;           // 接收缓冲区大小(建议 ≥ 4096,根据业务调整)
    unsigned int id;                 // 协议ID(内部使用,可设为 0 自动分配)
    void *user;                      // 全局用户数据(可选,所有连接共享)
    size_t tx_packet_size;           // 发送数据包大小(0 表示默认)
};
*/

lws_protocols WebSocketServer::protocols[] = {
    {
        "ws-protocol",
        WebSocketServer::static_callback,
        sizeof(void*),               // user 只用来存储 this 指针,所以设置为指针大小即可    
        1024 * 1024,
    },
    { NULL, NULL, 0, 0 }
};

WebSocketServer::WebSocketServer(int port, bool use_ssl) 
    : m_port(port), m_use_ssl(use_ssl), m_running(false), m_context(nullptr) {
}

WebSocketServer::~WebSocketServer() {
    stop();
}

bool WebSocketServer::start() {
    if (m_running) {
        std::cerr << "Server is already running!" << std::endl;
        return false;
    }

    // 初始化上下文创建信息
    lws_context_creation_info info = {};
    info.port = m_port;
    info.iface = NULL; // 监听所有网络接口
    info.protocols = protocols;
    info.gid = -1;
    info.uid = -1;
    info.user = this;
    
    // SSL 配置
    if (m_use_ssl) {
        info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
        // info.ssl_cert_filepath = "server.crt";
        // info.ssl_private_key_filepath = "server.key";
    }

    // 创建 libwebsockets 上下文
    m_context = lws_create_context(&info);
    if (!m_context) {
        std::cerr << "Failed to create libwebsockets context!" << std::endl;
        return false;
    }

    m_running = true;
    std::cout << "WebSocket server started on port " << m_port << std::endl;
    // 这里会阻塞主线程,也可以开一个线程处理
    while (m_running && m_context) {
        // 处理事件,超时时间为50毫秒
        lws_service(m_context, 50);
    }
    return true;
}

void WebSocketServer::stop() {
    if (m_running) {
        m_running = false;
        if (m_context) {
            std::this_thread::sleep_for(std::chrono::milliseconds(100));
            lws_context_destroy(m_context);
            m_context = nullptr;
        }
        std::cout << "WebSocket server stopped." << std::endl;
    }
}

int WebSocketServer::static_callback(lws* wsi, lws_callback_reasons reason, 
                                   void* user, void* in, size_t len) {
    lws_context* context = lws_get_context(wsi);
    WebSocketServer* server = static_cast<WebSocketServer*>(lws_context_user(context));
    if (server) {
        return server->instance_callback(wsi, reason, user, in, len);
    }
    return 0;
}

int WebSocketServer::instance_callback(lws* wsi, lws_callback_reasons reason, 
                                     void* user, void* in, size_t len) {
    switch (reason) {
        case LWS_CALLBACK_ESTABLISHED:
            {
                std::lock_guard<std::shared_mutex> lock(m_shareMutex);
                std::string clientId = getUId();
                m_clients[clientId] = wsi;
                m_msgQueues[wsi] = std::queue<std::string>();
                for (const auto& callback : m_connectedCallbacks) {
                    std::thread t([=]() {
                        callback(clientId);
                    });
                    t.detach();
                }
            }
            break;
            
        case LWS_CALLBACK_RECEIVE:
            handleReceive(wsi, in, len);
            break;
            
        case LWS_CALLBACK_SERVER_WRITEABLE:
            handleWritable(wsi);
            break;
            
        case LWS_CALLBACK_CLOSED:
            {
                std::lock_guard<std::shared_mutex> lock(m_shareMutex);
                for (auto it = m_clients.begin(); it != m_clients.end(); ++it) {
                    if (it->second == wsi) {
                        for (const auto& callback : m_closedCallbacks) {
                            std::thread t([=]() {
                                callback(it->first);
                            });
                            t.detach();
                        }
                        m_clients.erase(it->first);
                        break;
                    }
                }
                m_msgQueues.erase(wsi); // 清理连接对应的发送队列
            }
            break;
            
        default:
            break;
    }
    return 0;
}

void WebSocketServer::handleWritable(lws* wsi) {
    std::lock_guard<std::shared_mutex> lock(m_shareMutex);
    
    auto it = m_msgQueues.find(wsi);
    if (it == m_msgQueues.end() || it->second.empty()) {
        return;
    }
    std::queue<std::string>& queue = it->second;
    if (!queue.empty()) {
        const std::string& message = queue.front();
        // 准备发送缓冲区(必须预留 LWS_PRE 字节)
        unsigned char buf[LWS_PRE + message.size()];
        memcpy(buf + LWS_PRE, message.c_str(), message.size());
        
        // 发送消息
        int sent = lws_write(wsi, buf + LWS_PRE, message.size(), LWS_WRITE_TEXT);
        if (sent < 0) {
            std::cerr << "Failed to send message!" << std::endl;
        } else {
            //std::cout << "Message sent successfully, length: " << sent << std::endl;
        }
        
        queue.pop(); // 移除已发送的消息
        if (!queue.empty()) {
            lws_callback_on_writable(wsi);
        }
    }
}

void WebSocketServer::handleReceive(lws* wsi, void* in, size_t len) {
    if (lws_frame_is_binary(wsi)) {
        // 处理二进制消息
    } else {
        // 处理文本消息
        std::string message((const char*)in, len);
        std::lock_guard<std::shared_mutex> lock(m_shareMutex);
        for (auto it = m_clients.begin(); it != m_clients.end(); ++it) {
            if (it->second == wsi) {
                for (const auto& callback : m_receiveMsgCallback) {
                    std::thread t([=]() {
                        callback(it->first, message);
                    });
                    t.detach();
                }
                break;
            }
        }
    }
}

void WebSocketServer::wait() {
    while (m_running) {
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
}

bool WebSocketServer::sendMessage(const std::string& client, const std::string& message) {
    if (!m_running) {
        return false;
    }

    {
        std::lock_guard<std::shared_mutex> lock(m_shareMutex);
        auto it = m_clients.find(client);
        if (it == m_clients.end()) {
            return false;
        }
        lws *wsi = it->second;
        auto itsend = m_msgQueues.find(wsi);
        if (itsend != m_msgQueues.end()) {
            itsend->second.push(message);
        } else {
            return false; 
        }
        // 触发发送消息事件
        lws_callback_on_writable(wsi);
    }
    
    return true;
}

main 函数

cpp 复制代码
#include "WebSocketServer.h"

int main(int argc, char **argv)
{
    WebSocketServer server(1236);
    server.connectedCallback([](const std::string& id) {
        std::cout << "[连接] 客户端: " << id << std::endl;
    });
    
    server.receiveMsgCallback([&](const std::string& id, const std::string& msg) {
        server.sendMessage(id, msg);
    });

    server.closedCallback([](const std::string& id) {
        std::cout << "[断开] 客户端: " << id << std::endl;
    });
    server.start();
    return 0;
}
相关推荐
xixixi7777737 分钟前
解析常见的通信流量和流量分析
运维·开发语言·网络·安全·php·通信·流量
游戏开发爱好者839 分钟前
Charles抓包工具怎么用 Charles抓包教程、网络调试技巧与HTTPS配置全流程
网络·ios·小程序·https·uni-app·php·webview
_不会dp不改名_1 小时前
HCIP笔记7--OSPF特殊区域(nssa、totally nssa)、路由汇总
网络·智能路由器·hcip
aml258__1 小时前
一、Cisco(OSPF DR选举机制深度实验:从原理到配置的完整指南)251202
运维·网络·智能路由器·路由协议·ospf协议·dr选举机制·ccnp实验
闲人编程1 小时前
TCP/UDP网络编程实战
服务器·网络·tcp/ip·udp·客户端·codecapsule
星释1 小时前
Rust 练习册 103:维吉尼亚密码与安全通信
网络·安全·rust
深圳佛手1 小时前
Consul热更新的原理与实现
java·linux·网络
DX_水位流量监测1 小时前
管网液位计的技术特性与工程应用解析
大数据·网络·人工智能·信息可视化·架构
达不溜的日记1 小时前
BootLoader—基于CAN的FBL详解
网络·stm32·嵌入式硬件·mcu·车载系统·软件工程·信息与通信