目录
1、 异步服务器简介
2、异步服务器开发
2.1 会话类
2.1.1 会话类头文件
2.1.2 会话类源文件
2.2 服务类
2.2.1 服务类头文件
2.2.2 服务类源文件
2.3 主函数
3、异步服务器测试
4、当前异步服务器存在的问题及后续优化
boost 异步服务器分为会话类、服务类, 会话类主要负责与客户端通信, 服务类用来接收客户端连接。
/**
* @file session.h
* @author wangdong ([email protected])
* @brief 异步服务器开发 会话类
* @version 0.1
* @date 2023-06-20
*
* @copyright Copyright (c) 2023
*
*/
#ifndef __SESSION_H__
#define __SESSION_H__
#include "boost/asio.hpp"
#include
#include
#include "boost/uuid/random_generator.hpp"
#include "boost/uuid/uuid_io.hpp"
using boost::asio::ip::tcp;
class CServer;
class CSession
{
public:
CSession(boost::asio::io_context &ioc, CServer *pServer);
/**
* @brief Get the Socket object
*
* @return tcp::socket&
*/
tcp::socket &GetSocket() { return m_socket; }
/**
* @brief 开始接收客户端消息
*
*/
void Start();
/**
* @brief Get the Uuid object
*
* @return std::string 会话类唯一ID
*/
std::string GetUuid() { return m_strUuid; }
private:
/**
* @brief 处理读回调
*
* @param ec 读回调故障码
* @param bytes 读取字节
*/
void HandleRead(const boost::system::error_code &ec, size_t bytes);
/**
* @brief 处理写回调
*
* @param ec 写回调故障码
*/
void HandleWrite(const boost::system::error_code &ec);
private:
// 创建通信socket
tcp::socket m_socket;
// 创建服务器指针
CServer *m_pServer;
// 会话类唯一ID
std::string m_strUuid;
enum
{
MAX_BUFFER_SIZE = 1024,
};
// 读缓冲区
char m_arrBuffer[MAX_BUFFER_SIZE];
};
#endif /* __SESSION_H__ */
#include "session.h"
#include "server.h"
CSession::CSession(boost::asio::io_context &ioc, CServer *pServer)
: m_socket(ioc), m_pServer(pServer)
{
// 生成唯一ID, 也可以自己实现雪花算法
boost::uuids::uuid uuid = boost::uuids::random_generator()();
m_strUuid = boost::uuids::to_string(uuid);
// 初始化读缓冲区
memset(m_arrBuffer, 0, MAX_BUFFER_SIZE);
}
void CSession::Start()
{
m_socket.async_read_some(
boost::asio::buffer(m_arrBuffer, MAX_BUFFER_SIZE),
std::bind(&CSession::HandleRead, this, std::placeholders::_1, std::placeholders::_2));
}
void CSession::CSession::HandleRead(const boost::system::error_code &ec, size_t bytes)
{
if (!ec)
{
std::cout << "recv:" << m_socket.remote_endpoint().address().to_string() << ":" << m_arrBuffer << std::endl;
m_socket.async_write_some(boost::asio::buffer(m_arrBuffer, bytes),
std::bind(&CSession::HandleWrite, this, std::placeholders::_1));
}
else
{
std::cout << "error: " << ec.message() << std::endl;
m_pServer->ClearSession(m_strUuid);
}
}
void CSession::HandleWrite(const boost::system::error_code &ec)
{
if (!ec)
{
m_socket.async_read_some(
boost::asio::buffer(m_arrBuffer, MAX_BUFFER_SIZE),
std::bind(&CSession::HandleRead, this, std::placeholders::_1, std::placeholders::_2));
// 初始化读缓冲区
memset(m_arrBuffer, 0, MAX_BUFFER_SIZE);
}
else
{
std::cout << "error: " << ec.message() << std::endl;
m_pServer->ClearSession(m_strUuid);
}
}
/**
* @file server.h
* @author wangdong ([email protected])
* @brief 服务器开发
* @version 0.1
* @date 2023-06-20
*
* @copyright Copyright (c) 2023
*
*/
#ifndef __SERVER_H__
#define __SERVER_H__
#include "session.h"
#include
#include "server.h"
CServer::CServer(boost::asio::io_context &ioc, unsigned short usPort)
: m_ioc(ioc), m_acceptor(ioc, tcp::endpoint(tcp::v4(), usPort))
{
StartAccept();
}
void CServer::ClearSession(const std::string &strUuid)
{
m_mapSessions.erase(strUuid);
}
void CServer::StartAccept()
{
// 创建会话类
std::shared_ptr pSession = std::make_shared(m_ioc, this);
m_acceptor.async_accept(pSession->GetSocket(), std::bind(&CServer::HandleAccept,
this, pSession, std::placeholders::_1));
}
void CServer::HandleAccept(std::shared_ptr pSession,
const boost::system::error_code &ec)
{
if (!ec)
{
pSession->Start();
m_mapSessions.insert(std::make_pair(pSession->GetUuid(), pSession));
}
else
{
std::cerr << "Exception: " << ec.message() << std::endl;
}
// 再次等待接收
StartAccept();
}
/**
* @file main.cpp
* @author wangdong ([email protected])
* @brief 异步服务器开发 程序入口
* @version 0.1
* @date 2023-06-20
*
* @copyright Copyright (c) 2023
*
*/
#include "server.h"
int main(int argc, char *argv[])
{
try
{
// 创建IO上下文
boost::asio::io_context ioc;
// 创建服务器
CServer server(ioc, 10086);
// 处理事件循环并阻塞主线程
ioc.run();
}
catch (const std::exception &e)
{
std::cerr << "Exception: " << e.what() << '\n';
}
return 0;
}
当前服务器存在的问题:
1) 当前服务器无法主动发送消息,知道靠收到客户端消息的回调函数来发送消息,现实中不实用。
2) 当前服务器未实现伪闭包延长CSession的生命周期,有可能写回调释放了CSession类后,读回调还会调用。
3)当前服务器未实现粘包、粘包的处理。
综上会在后续博客中优化。