【work记录:c++web聊天服务器】将boost作为中间层处理websocket解析与客户端数据收发

日期:2025.4.16

个人总结:

幻影忍者前情提要:

是这样的,实在是拖更太久了。

之前自己写实现Reactor服务器之后,自己做完了之后又跑去做了别的项目,都是跟着网上的教程去做的,有什么Rpc网络通信框架,集群聊天服务器之类的,中间虚拟机崩溃了一次导致我的代码也无了,就一个集群聊天服务器的内容还有(之前的没上传github)。手写muduo库的这个内容也不见了。目前并不是打算重新去写,打算以后重新整合的时候再去做。

于是自己的一些别的想法使得我对于之前拖更的内容并没有更新(题解倒是更了一些(bushi)),于是打算现在开始搞一个work记录,不再是要求一个完整的学习的记录就发出来,纯给自己看。

但是还是说一下大致的前提:

之前自己做了一个集群聊天服务器,跟着网上的施磊老师的教程做的,但是这是一个命令行实现的服务器,没有什么好看的界面,本来想用Qt做的,但是下了半天,电脑没容量(阿巴。于是打算用web搞一个。之后搞成了web版本的聊天服务器之后,由于那个是用json写的,所以应该还会再改成protobuf。然后再结合一下之前的rpc框架,看看能不能搞一个分布式的服务器出来。在之后,目前就没有想法了吧。这个是为了看能不能找到实习搞的,或者也有可能搞完web版本的就不干了也有可能(时间可能不够。

在之后打算数据库的方向了解了解,或者是分布式存储的方向。这都是后话了。

目前进度:

说一下目前的进度:

目前由于想弄一个web版本的,所以找上了boost库(因为他有websocket解析嘛),但是之前的muduo并不打算完全舍弃,毕竟boost库这个东西我是目前希望能用就行,先不想着把muduo库全换掉。

于是目前的想法是用boost去做一个中间层,用来做协议解析和与客户端之间的数据收发,然后muduo那个服务器就全部用于业务处理了。

有一些todo:

  • 之前muduo的聊天服务器里有个map,要把map的value改成字符串"uid",这个uid是在boost服务器里用到的,boost服务器会根据这个uid找到客户端对应连接的connection(一个封装的类,跟session差不多)
  • muduo服务器要改成收发数据需要用到前缀长度

另外一提关于这个uid,本来是打算弃用的,但是后来想了半天,我不希望boost服务器和muduo服务器的耦合性很高,毕竟如果登录这个功能和别的业务功能拆开,然后boost服务器上单独留一个对于登录功能的处理,会让我很难受,就是吃了屎一样。服务器的设置一定也会很烂。所以后来暂时想的办法,就是把之前muduo服务器里的map,key是userid,value是TcpConnection,改成了uid(uid是客户端对应连接的一个随机生成的unique字符串)。然后在muduo向boost传输数据的时候加上一个uid,然后boost就可以根据这个uid去查询了,感觉还不戳。

今天干了快一天了,从boost啥都不懂,到现在虽然代码我写不出来,但是我会魔改啊。

boost的代码借鉴了llfc的webserver,然后魔改了一番

class ConnectionMgr
{
public:
    static ConnectionMgr &GetInstance();
    // boost::asio::thread_pool &GetPool();
    void AddConnection(std::shared_ptr<Connection> conptr);
    void RmvConnection(std::string);

    void Send(std::shared_ptr<Connection> conptr, std::string msg);
    void SendToMuduo(std::string);
    void RecvFromMuduo();

private:
    ConnectionMgr(const ConnectionMgr &) = delete;
    ConnectionMgr &operator=(const ConnectionMgr &) = delete;
    ConnectionMgr();
    void ConnectToMuduo(const std::string &ip, int port);
    boost::unordered_map<std::string, std::shared_ptr<Connection>> _map_cons;
    // boost::asio::thread_pool _thread_pool;
    boost::asio::io_context _io_context;
    boost::asio::ip::tcp::socket _muduo_socket;

    std::vector<char> recv_buf;
    int expect_recv_len = 0;
};

在原本的基础上加了muduo的socket,还有对muduo的一些send和recv函数。还有一些杂七杂八的。

然后相对应的改了改,便于我这个服务器的实现,修改了一些地方,具体了我也忘了,毕竟原本是一个echo,很多地方并不太适用我的想法。

花絮:

本来思路很乱,甚至没有想着往这个ConnectionMgr上改,还想着在Connection上用一个线程池来防止这个IO线程的阻塞,(毕竟原代码把数据处理和IO放到了一个线程里,那性能肯定会很糟糕呢)。

一直想着等到项目成型了,再去慢慢写blog去补回来,但是感觉越做越会鸽掉,为了不鸽!!!!!!!!

其他的代码变化不大,这里放一下cpp文件:

#include "connectionmgr.h"
#include "nlohmann/json.hpp"
using json = nlohmann::json;

ConnectionMgr &ConnectionMgr::GetInstance()
{
    static ConnectionMgr instance;
    return instance;
}

void ConnectionMgr::AddConnection(std::shared_ptr<Connection> conptr)
{
    _map_cons[conptr->GetUid()] = conptr;
}

void ConnectionMgr::RmvConnection(std::string id)
{
    _map_cons.erase(id);
}

ConnectionMgr::ConnectionMgr() : _muduo_socket(_io_context)
{
    std::string _ip = "192.168.11.133";
    int _port = 5006;
    ConnectToMuduo(_ip, _port);
}

// boost::asio::thread_pool &ConnectionMgr::GetPool()
// {
//     return _thread_pool;
// }

void ConnectionMgr::Send(std::shared_ptr<Connection> conptr, std::string msg)
{
    conptr->AsyncSend(std::move(msg));
}
void ConnectionMgr::ConnectToMuduo(const std::string &ip, int port)
{
    _muduo_socket.async_connect(
        boost::asio::ip::tcp::endpoint(boost::asio::ip::make_address(ip), port),
        [this](boost::system::error_code ec)
        {
            if (!ec)
            {
                RecvFromMuduo(); // 开始监听 Muduo 的响应
            }
        });
}

void ConnectionMgr::SendToMuduo(std::string msg)
{
    int send_len = msg.size();
    std::string header_msg((char *)&send_len);
    header_msg += msg;
    boost::asio::async_write(
        _muduo_socket,
        boost::asio::buffer(header_msg),
        [](boost::system::error_code ec, size_t bytes_write)
        {
            if (ec)
            {
                // 处理发送失败(例如重试或记录日志)
                std::cout << "SendToMsg 发送失败" << std::endl;
            }
        });
}

void ConnectionMgr::RecvFromMuduo()
{
    // 计算还需要读取的字节数
    const size_t bytes_needed = (expect_recv_len == 0) ? 4 : expect_recv_len;

    // 异步读取
    boost::asio::async_read(
        _muduo_socket,
        boost::asio::buffer(recv_buf.data() + recv_buf.size(), bytes_needed),
        [this](boost::system::error_code ec, size_t bytes_read)
        {
            if (!ec)
            {
                if (expect_recv_len == 0)
                {
                    // 读取长度头(前4字节)
                    expect_recv_len = *reinterpret_cast<uint32_t *>(recv_buf.data());
                    recv_buf.resize(4 + expect_recv_len); // 预留空间
                    RecvFromMuduo();                      // 继续读取 JSON 内容
                }
                else
                {
                    // 完整读取 JSON 内容
                    std::string json_str(recv_buf.begin() + 4, recv_buf.end());
                    recv_buf.clear();
                    expect_recv_len = 0;
                    json msg = json::parse(json_str);
                    std::string to_uid = msg["to_uid"].get<std::string>();
                    auto conptr = _map_cons[to_uid];
                    msg.erase("to_uid");
                    Send(conptr, msg.dump());

                    // 继续监听下一条消息
                    RecvFromMuduo();
                }
            }
            else
            {
                // 错误处理(同之前)
                std::cout << "Muduo 连接错误: " << ec.message() << std::endl;
                _muduo_socket.close();
                // std::this_thread::sleep_for(std::chrono::seconds(5));
                // ConnectToMuduo("192.168.11.133", 5006);
            }
        });
}

其实我在想,如果用java写这些内容会不会比较简单,自己会不会更适合去做java后端而不是在这里硬着头皮去弄C++,没有必要因为自己ICPC就将来搞C++。

但是都已经学到这里了,为什么不坚持一把呢?

应该自信一点。

posted @ 2025-04-16 02:32  AdviseDY  阅读(45)  评论(0)    收藏  举报