C++ - 使用Websocket++编写客户端连接WebSocket服务器并进行通信

文章目录
1 库依赖
2 根据官方示例代码修改封装的WebsocketClient类
2.1 WebsocketClient代码
2.2 WebsocketClient类使用代码
3 Websocket++官方编写客户端示例教程
4 与Websocket++官方示例客户端的不同
 

1 库依赖
Websocket++/Websocketpp依赖于boost(使用boost 1.74),Websocket++ 0.8.2版本,因为暂时没有使用wss,所以没有集成Openssl。

2 根据官方示例代码修改封装的WebsocketClient类
2.1 WebsocketClient代码
WebsocketClient.h
#ifndef WEBSOCKET_CLIENT_H
#define WEBSOCKET_CLIENT_H
 
// 不包含TLS Client
#include <websocketpp/config/asio_no_tls_client.hpp>
#include <websocketpp/client.hpp>
 
// 包含TLS Client
// #include <websocketpp/config/asio_client.hpp>
// #include <websocketpp/client.hpp>
 
#include <websocketpp/common/thread.hpp>
#include <websocketpp/common/memory.hpp>
 
 
#include <string>
#include <locale>
#include <codecvt>
 
typedef websocketpp::client<websocketpp::config::asio_client> client;
 
static std::wstring string_to_wstring(const std::string &s)
{
    using default_convert = std::codecvt<wchar_t, char, std::mbstate_t>;
    static std::wstring_convert<default_convert>conv(new default_convert("CHS"));
    return conv.from_bytes(s);
}
static std::string wstring_to_string(const std::wstring &s)
{
    using default_convert = std::codecvt<wchar_t, char, std::mbstate_t>;
    static std::wstring_convert<default_convert>conv(new default_convert("CHS"));
    return conv.to_bytes(s);
}
static std::string ansi_to_utf8(const std::string &s)
{
    static std::wstring_convert<std::codecvt_utf8<wchar_t> > conv;
    return conv.to_bytes(string_to_wstring(s));
}
static std::string utf8_to_ansi(const std::string& s)
{
    static std::wstring_convert<std::codecvt_utf8<wchar_t> > conv;
    return wstring_to_string(conv.from_bytes(s));
}
 
 
// 保存一个连接的metadata
class connection_metadata {
public:
    typedef websocketpp::lib::shared_ptr<connection_metadata> ptr;
 
    connection_metadata(websocketpp::connection_hdl hdl, std::string url)
        : m_Hdl(hdl)
        , m_Status("Connecting")
        , m_Url(url)
        , m_Server("N/A")
    {}
 
    void on_open(client * c, websocketpp::connection_hdl hdl) 
    {
        m_Status = "Open";
 
        client::connection_ptr con = c->get_con_from_hdl(hdl);
        m_Server = con->get_response_header("Server");
    }
 
    void on_fail(client * c, websocketpp::connection_hdl hdl)
    {
        m_Status = "Failed";
 
        client::connection_ptr con = c->get_con_from_hdl(hdl);
        m_Server = con->get_response_header("Server");
        m_Error_reason = con->get_ec().message();
    }
 
    void on_close(client * c, websocketpp::connection_hdl hdl)
    {
        m_Status = "Closed";
        client::connection_ptr con = c->get_con_from_hdl(hdl);
        std::stringstream s;
        s << "close code: " << con->get_remote_close_code() << " ("
            << websocketpp::close::status::get_string(con->get_remote_close_code())
            << "), close reason: " << con->get_remote_close_reason();
        m_Error_reason = s.str();
    }
 
    void on_message(websocketpp::connection_hdl, client::message_ptr msg)
    {
        if (msg->get_opcode() == websocketpp::frame::opcode::text) 
        {
            std::string message = utf8_to_ansi(msg->get_payload());
            std::cout << "收到来自服务器的消息:" << message << std::endl;
        }
        else 
        {
            std::string message = websocketpp::utility::to_hex(msg->get_payload());
        }
    }
 
    websocketpp::connection_hdl get_hdl() const
    {
        return m_Hdl;
    }
 
    std::string get_status() const
    {
        return m_Status;
    }
private:
    websocketpp::connection_hdl m_Hdl;  // websocketpp表示连接的编号
    std::string m_Status;               // 连接自动状态
    std::string m_Url;                  // 连接的URI
    std::string m_Server;               // 服务器信息
    std::string m_Error_reason;         // 错误原因
};
 
 
class WebsocketClient
{
public:
    WebsocketClient();
    virtual~WebsocketClient();
 
public:
    bool Connect(std::string const & url);
    bool Close(std::string reason = "");
    bool Send(std::string message);
 
    connection_metadata::ptr GetConnectionMetadataPtr();
 
private:
    connection_metadata::ptr m_ConnectionMetadataPtr;
    client m_WebsocketClient;
    websocketpp::lib::shared_ptr<websocketpp::lib::thread> m_Thread; // 线程
};
 
 
#endif // !WEBSOCKET_ENDPOINT_H
 
WebsocketClient.cpp
#include "WebsocketClient.h"
 
WebsocketClient::WebsocketClient()
{
    m_WebsocketClient.clear_access_channels(websocketpp::log::alevel::all);  // 开启全部接入日志级别
    m_WebsocketClient.clear_error_channels(websocketpp::log::elevel::all);   // 开启全部错误日志级别
 
    m_WebsocketClient.init_asio();       // 初始化asio
    m_WebsocketClient.start_perpetual(); // 避免请求为空时退出,实际上,也是避免asio退出
 
    // 独立运行client::run()的线程,主要是避免阻塞
    m_Thread = websocketpp::lib::make_shared<websocketpp::lib::thread>(&client::run, &m_WebsocketClient);
}
 
WebsocketClient::~WebsocketClient()
{
    m_WebsocketClient.stop_perpetual();
 
    if (m_ConnectionMetadataPtr != nullptr && m_ConnectionMetadataPtr->get_status() == "Open")
    {
        websocketpp::lib::error_code ec;
        m_WebsocketClient.close(m_ConnectionMetadataPtr->get_hdl(), websocketpp::close::status::going_away, "", ec); // 关闭连接
 
        if (ec) {
            std::cout << "> Error initiating close: " << ec.message() << std::endl;
        }
    }
 
    m_Thread->join();
}
 
bool WebsocketClient::Connect(std::string const & url)
{
    websocketpp::lib::error_code ec;
 
    // 创建connect的共享指针,注意,此时创建并没有实际建立
    client::connection_ptr con = m_WebsocketClient.get_connection(url, ec);  
 
    if (ec) {
        std::cout << "> Connect initialization error: " << ec.message() << std::endl;
        return false;
    }
 
    // 创建连接的metadata信息,并保存
    connection_metadata::ptr metadata_ptr = websocketpp::lib::make_shared<connection_metadata>(con->get_handle(), url);
    m_ConnectionMetadataPtr = metadata_ptr;
 
    // 注册连接打开的Handler
    con->set_open_handler(websocketpp::lib::bind(
        &connection_metadata::on_open,
        metadata_ptr,
        &m_WebsocketClient,
        websocketpp::lib::placeholders::_1
    ));
 
    // 注册连接失败的Handler
    con->set_fail_handler(websocketpp::lib::bind(
        &connection_metadata::on_fail,
        metadata_ptr,
        &m_WebsocketClient,
        websocketpp::lib::placeholders::_1
    ));
 
    // 注册连接关闭的Handler
    con->set_close_handler(websocketpp::lib::bind(
        &connection_metadata::on_close,
        metadata_ptr,
        &m_WebsocketClient,
        websocketpp::lib::placeholders::_1
    ));
 
    // 注册连接接收消息的Handler
    con->set_message_handler(websocketpp::lib::bind(
        &connection_metadata::on_message,
        metadata_ptr,
        websocketpp::lib::placeholders::_1,
        websocketpp::lib::placeholders::_2
    ));
 
    // 进行连接
    m_WebsocketClient.connect(con);
 
    std::cout << "Websocket连接成功" << std::endl;
 
    return true;
}
 
bool WebsocketClient::Close(std::string reason)
{
    websocketpp::lib::error_code ec;
 
    if (m_ConnectionMetadataPtr != nullptr)
    {
        int close_code = websocketpp::close::status::normal;
        // 关闭连接
        m_WebsocketClient.close(m_ConnectionMetadataPtr->get_hdl(), close_code, reason, ec);
        if (ec) {
            std::cout << "> Error initiating close: " << ec.message() << std::endl;
            return false;
        }
        std::cout << "关闭Websocket连接成功" << std::endl;
    }
 
    return true;
}
 
bool WebsocketClient::Send(std::string message)
{
    websocketpp::lib::error_code ec;
 
    if (m_ConnectionMetadataPtr != nullptr)
    {
        // 连接发送数据
        m_WebsocketClient.send(m_ConnectionMetadataPtr->get_hdl(), ansi_to_utf8(message), websocketpp::frame::opcode::text, ec);
        if (ec) {
            std::cout << "> Error sending message: " << ec.message() << std::endl;
            return false;
        }
        std::cout << "发送数据成功" << std::endl;
    }
 
    return true;
}
 
connection_metadata::ptr WebsocketClient::GetConnectionMetadataPtr()
{
    return m_ConnectionMetadataPtr;
}
 
2.2 WebsocketClient类使用代码
#include "WebsocketClient.h"
 
 
int main() {
    bool done = false;
    std::string input;
    WebsocketClient 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"
                << "connect <ws uri>\n"
                << "send <message>\n"
                << "close [<close code:default=1000>] [<close reason>]\n"
                << "help: Display this help text\n"
                << "quit: Exit the program\n"
                << std::endl;
        }
        else if (input.substr(0, 7) == "connect") {
            endpoint.Connect(input.substr(8));
        }
        else if (input.substr(0, 4) == "send") {
            std::stringstream ss(input);
 
            std::string cmd;
            int id;
            std::string message;
 
            ss >> cmd;
            std::getline(ss, message);
 
            endpoint.Send(message);
        }
        else if (input.substr(0, 5) == "close") {
            std::stringstream ss(input);
 
            std::string cmd;
            std::string reason;
 
            ss >> cmd ;
            std::getline(ss, reason);
 
            endpoint.Close(reason);
        }
        else if (input.substr(0, 4) == "quit") {
            done = true;
        }
        else {
            std::cout << "> Unrecognized Command" << std::endl;
        }
    }
 
    return 0;
}
3 Websocket++官方编写客户端示例教程
可参考链接:https://docs.websocketpp.org/md_tutorials_utility_client_utility_client.html
也可在Websocket++的文件夹\tutorials\utility_client找到该示例!
或者在Websocket++的文件夹\example\scratch_client找到该示例!

4 与Websocket++官方示例客户端的不同
增加ansi与utf-8编码转换,中文字符不再乱码
不适用官方使用id管理多个链接方式,这里认为的客户端只有一个长连接,而WebsocketClient只维护一个长连接即可
2020年9月16日更新:修改WebsocketClient类

修改信息

将open,close,message等回调函数移动到WebsocketClient内部
新增设置回调函数的公共接口
WebsocketClient.h

#ifndef WEBSOCKET_CLIENT_H
#define WEBSOCKET_CLIENT_H
 
// 不包含TLS Client
#include <websocketpp/config/asio_no_tls_client.hpp>
#include <websocketpp/client.hpp>
 
// 包含TLS Client
// #include <websocketpp/config/asio_client.hpp>
// #include <websocketpp/client.hpp>
 
#include <websocketpp/common/thread.hpp>
#include <websocketpp/common/memory.hpp>
 
 
#include <string>
#include <locale>
#include <codecvt>
 
typedef websocketpp::client<websocketpp::config::asio_client> client;
 
static std::wstring string_to_wstring(const std::string &s)
{
    using default_convert = std::codecvt<wchar_t, char, std::mbstate_t>;
    static std::wstring_convert<default_convert>conv(new default_convert("CHS"));
    return conv.from_bytes(s);
}
static std::string wstring_to_string(const std::wstring &s)
{
    using default_convert = std::codecvt<wchar_t, char, std::mbstate_t>;
    static std::wstring_convert<default_convert>conv(new default_convert("CHS"));
    return conv.to_bytes(s);
}
static std::string ansi_to_utf8(const std::string &s)
{
    static std::wstring_convert<std::codecvt_utf8<wchar_t> > conv;
    return conv.to_bytes(string_to_wstring(s));
}
static std::string utf8_to_ansi(const std::string& s)
{
    static std::wstring_convert<std::codecvt_utf8<wchar_t> > conv;
    return wstring_to_string(conv.from_bytes(s));
}
 
typedef std::function<void()> OnOpenFunc;
typedef std::function<void()> OnFailFunc;
typedef std::function<void()> OnCloseFunc;
typedef std::function<void(const std::string& message)> OnMessageFunc;
 
class WebsocketClient 
{
public:
    WebsocketClient();
    virtual~WebsocketClient();
 
public:
    bool Connect(std::string const & url);
    bool Close(std::string reason = "");
    bool Send(std::string message);
 
    connection_metadata::ptr GetConnectionMetadataPtr();
 
    void OnOpen(client * c, websocketpp::connection_hdl hdl);
    void OnFail(client * c, websocketpp::connection_hdl hdl);
    void OnClose(client * c, websocketpp::connection_hdl hdl);
    void OnMessage(websocketpp::connection_hdl, client::message_ptr msg);
 
 
    void SetOnOpenFunc(OnOpenFunc func);
    void SetOnFailFunc(OnFailFunc func);
    void SetOnCloseFunc(OnCloseFunc func);
    void SetMessageFunc(OnMessageFunc func);
private:
    connection_metadata::ptr m_ConnectionMetadataPtr;
    client m_WebsocketClient;
    websocketpp::lib::shared_ptr<websocketpp::lib::thread> m_Thread; // 线程
 
    OnOpenFunc m_OnOpenFunc;
    OnFailFunc m_OnFailFunc;
    OnCloseFunc m_OnCloseFunc;
    OnMessageFunc m_MessageFunc;
};
#endif // !WEBSOCKET_ENDPOINT_H
 
WebsocketClient.cpp

#include "WebsocketClient.h"
 
WebsocketClient::WebsocketClient()
{
    m_WebsocketClient.clear_access_channels(websocketpp::log::alevel::all);  // 开启全部接入日志级别
    m_WebsocketClient.clear_error_channels(websocketpp::log::elevel::all);   // 开启全部错误日志级别
 
    m_WebsocketClient.init_asio();       // 初始化asio
    m_WebsocketClient.start_perpetual(); // 避免请求为空时退出,实际上,也是避免asio退出
 
    // 独立运行client::run()的线程,主要是避免阻塞
    m_Thread = websocketpp::lib::make_shared<websocketpp::lib::thread>(&client::run, &m_WebsocketClient);
 
    m_OnOpenFunc = nullptr;
    m_OnFailFunc = nullptr;
    m_OnCloseFunc = nullptr;
    m_MessageFunc = nullptr;
}
 
WebsocketClient::~WebsocketClient()
{
    m_WebsocketClient.stop_perpetual();
 
    if (m_ConnectionMetadataPtr != nullptr && m_ConnectionMetadataPtr->get_status() == "Open")
    {
        websocketpp::lib::error_code ec;
        m_WebsocketClient.close(m_ConnectionMetadataPtr->get_hdl(), websocketpp::close::status::going_away, "", ec); // 关闭连接
 
        if (ec) {
            std::cout << "> Error initiating close: " << ec.message() << std::endl;
        }
    }
 
    m_Thread->join();
}
 
bool WebsocketClient::Connect(std::string const & url)
{
    websocketpp::lib::error_code ec;
 
    // 创建connect的共享指针,注意,此时创建并没有实际建立
    client::connection_ptr con = m_WebsocketClient.get_connection(url, ec);  
 
    if (ec) {
        std::cout << "> Connect initialization error: " << ec.message() << std::endl;
        return false;
    }
 
    // 创建连接的metadata信息,并保存
    connection_metadata::ptr metadata_ptr = websocketpp::lib::make_shared<connection_metadata>(con->get_handle(), url);
    m_ConnectionMetadataPtr = metadata_ptr;
 
    // 注册连接打开的Handler
    //con->set_open_handler(websocketpp::lib::bind(
    //    &connection_metadata::on_open,
    //    metadata_ptr,
    //    &m_WebsocketClient,
    //    websocketpp::lib::placeholders::_1
    //));
    con->set_open_handler(websocketpp::lib::bind(
        &WebsocketClient::OnOpen,
        this,
        &m_WebsocketClient,
        websocketpp::lib::placeholders::_1
    ));
 
    // 注册连接失败的Handler
    //con->set_fail_handler(websocketpp::lib::bind(
    //    &connection_metadata::on_fail,
    //    metadata_ptr,
    //    &m_WebsocketClient,
    //    websocketpp::lib::placeholders::_1
    //));
    con->set_fail_handler(websocketpp::lib::bind(
        &WebsocketClient::OnFail,
        this,
        &m_WebsocketClient,
        websocketpp::lib::placeholders::_1
    ));
 
    // 注册连接关闭的Handler
    //con->set_close_handler(websocketpp::lib::bind(
    //    &connection_metadata::on_close,
    //    metadata_ptr,
    //    &m_WebsocketClient,
    //    websocketpp::lib::placeholders::_1
    //));
    con->set_close_handler(websocketpp::lib::bind(
        &WebsocketClient::OnClose,
        this,
        &m_WebsocketClient,
        websocketpp::lib::placeholders::_1
    ));
 
    // 注册连接接收消息的Handler
    //con->set_message_handler(websocketpp::lib::bind(
    //    &connection_metadata::on_message,
    //    metadata_ptr,
    //    websocketpp::lib::placeholders::_1,
    //    websocketpp::lib::placeholders::_2
    //));
    con->set_message_handler(websocketpp::lib::bind(
        &WebsocketClient::OnMessage,
        this,
        websocketpp::lib::placeholders::_1,
        websocketpp::lib::placeholders::_2
    ));
 
    // 进行连接
    m_WebsocketClient.connect(con);
 
    std::cout << "Websocket连接成功" << std::endl;
 
    // 注意,不能在Websocket连接完成之后马上就发送消息,不然会出现Invalid State的错误,
    // 导致消息发送不成功,所以在连接成功之后,主线程休眠1秒
    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
 
    return true;
}
 
bool WebsocketClient::Close(std::string reason)
{
    websocketpp::lib::error_code ec;
 
    if (m_ConnectionMetadataPtr != nullptr)
    {
        int close_code = websocketpp::close::status::normal;
        // 关闭连接
        m_WebsocketClient.close(m_ConnectionMetadataPtr->get_hdl(), close_code, reason, ec);
        if (ec) {
            std::cout << "> Error initiating close: " << ec.message() << std::endl;
            return false;
        }
        std::cout << "关闭Websocket连接成功" << std::endl;
    }
 
    return true;
}
 
bool WebsocketClient::Send(std::string message)
{
    websocketpp::lib::error_code ec;
 
    if (m_ConnectionMetadataPtr != nullptr)
    {
        // 连接发送数据
        m_WebsocketClient.send(m_ConnectionMetadataPtr->get_hdl(),ansi_to_utf8(message), websocketpp::frame::opcode::text, ec);
        if (ec) 
        {
            std::cout << "> Error sending message: " << ec.message() << std::endl;
            std::string errorMessage = ec.message();
            return false;
        }
        std::cout << "发送数据成功" << std::endl;
    }
 
    return true;
}
 
connection_metadata::ptr WebsocketClient::GetConnectionMetadataPtr()
{
    return m_ConnectionMetadataPtr;
}
 
void WebsocketClient::OnOpen(client * c, websocketpp::connection_hdl hdl)
{
    if (m_OnOpenFunc != nullptr)
    {
        m_OnOpenFunc();
    }
}
 
void WebsocketClient::OnFail(client * c, websocketpp::connection_hdl hdl)
{
    if (m_OnFailFunc != nullptr)
    {
        m_OnFailFunc();
    }
}
 
void WebsocketClient::OnClose(client * c, websocketpp::connection_hdl hdl)
{
    if (m_OnCloseFunc != nullptr)
    {
        m_OnCloseFunc();
    }
}
 
void WebsocketClient::OnMessage(websocketpp::connection_hdl, client::message_ptr msg)
{
    if (msg->get_opcode() == websocketpp::frame::opcode::text)
    {
        std::string message = utf8_to_ansi(msg->get_payload());
        //std::cout << "收到来自服务器的消息:" << message << std::endl;
 
        if (m_MessageFunc != nullptr)
        {
            m_MessageFunc(message);
        }    
    }
    else
    {
        std::string message = websocketpp::utility::to_hex(msg->get_payload());
    }
}
 
void WebsocketClient::SetOnOpenFunc(OnOpenFunc func)
{
    m_OnOpenFunc = func;
}
 
void WebsocketClient::SetOnFailFunc(OnFailFunc func)
{
    m_OnFailFunc = func;
}
 
void WebsocketClient::SetOnCloseFunc(OnCloseFunc func)
{
    m_OnCloseFunc = func;
}
 
void WebsocketClient::SetMessageFunc(OnMessageFunc func)
{
    m_MessageFunc = func;
}
 
另外在应用该类的时候发现了一个Websocket++的一个问题: 不能在Websocket连接完成之后马上就发送消息,不然会出现Invalid State的错误,导致消息发送不成功,所以在连接成功之后,主线程休眠1秒,这在代码中也有体现。
————————————————
版权声明:本文为CSDN博主「huang714」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/huang714/article/details/109525126

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值