【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++。
但是都已经学到这里了,为什么不坚持一把呢?
应该自信一点。

浙公网安备 33010602011771号