系列文章目录:C++ asio网络编程-CSDN博客
在上一小节中,我们介绍了boost::asio中的异步读写api,这一小节就是使用这些api去实现一个异步读写的server。
1、Session类
Session类主要是处理客户端消息收发的会话类,为了简单起见,我们不考虑粘包问题,也不考虑支持手动调用发送的接口,只以应答的方式发送和接收固定长度(1024字节长度)的数据。
class Session
{
public:
Session(boost::asio::io_context& ioc);
boost::asio::ip::tcp::socket& getSocket();
void start();
private:
void handle_read(const boost::system::error_code& ec, std::size_t bytes_transferred);
void handle_write(const boost::system::error_code& ec);
boost::asio::ip::tcp::socket _socket;
enum {MAX_LENGTH = 1024};
char _data[MAX_LENGTH];
};
- _data用来接收客户端传递的数据
- _socket为单独处理客户端读写的socket。
- handle_read和handle_write分别为读回调函数和写回调函数。
接下来我们去是实现他们
Session::Session(boost::asio::io_context& ioc) : _socket(ioc)
{
}
boost::asio::ip::tcp::socket& Session::getSocket()
{
return _socket;
}
// 在Start方法中我们调用异步读操作,
// 监听对端发送的消息。当对端发送数据后,触发handle_read函数
void Session::start()
{
memset(_data, 0, MAX_LENGTH);
_socket.async_read_some(
boost::asio::buffer(_data, MAX_LENGTH),
std::bind(&Session::handle_read, this, std::placeholders::_1, std::placeholders::_2)
);
}
// handle_read函数内将收到的数据发送给对端,
// 当发送完成后触发handle_write回调函数。
void Session::handle_read(const boost::system::error_code& ec, std::size_t bytes_transferred)
{
if (!ec) {
std::cout << "server receive data is " << _data << std::endl;
boost::asio::async_write(_socket,
boost::asio::buffer(_data, bytes_transferred),
std::bind(&Session::handle_write, this, std::placeholders::_1));
}
else {
std::cout << "read error" << std::endl;
delete this;
}
}
// handle_write函数内又一次监听了读事件,
// 如果对端有数据发送过来则触发handle_read,我们再将收到的数据发回去。
// 从而达到应答式服务的效果。
void Session::handle_write(const boost::system::error_code& ec)
{
if (!ec) {
// 发完后继续去读
memset(_data, 0, MAX_LENGTH);
_socket.async_read_some(
boost::asio::buffer(_data, MAX_LENGTH),
std::bind(&Session::handle_read, this, std::placeholders::_1, std::placeholders::_2)
);
}
else {
std::cout << "write error" << ec.value() << std::endl;
delete this;
}
}
2、Server类
Server类为服务器接收连接的管理类
class Server
{
public:
Server(boost::asio::io_context& ioc, unsigned short port);
private:
void start_accept();
void handle_accept(Session* new_session, const boost::system::error_code& ec);
boost::asio::io_context& _ioc; // io_context不允许被复制
boost::asio::ip::tcp::acceptor _acceptor;
};
- start_accept将要接收连接的acceptor绑定到服务上。
- handle_accept为新连接到来后触发的回调函数。
接下来是函数实现
Server::Server(boost::asio::io_context& ioc, unsigned short port)
: _ioc(ioc),
_acceptor(ioc, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port))
{
std::cout << "Server start success, on port: " << port << std::endl;
start_accept();
}
void Server::start_accept()
{
Session* new_session = new Session(_ioc);
_acceptor.async_accept(new_session->getSocket(),
std::bind(&Server::handle_accept, this, new_session,std::placeholders::_1));
}
void Server::handle_accept(Session* new_session, const boost::system::error_code& ec)
{
if (!ec) {
new_session->start();
}
else {
delete new_session;
}
start_accept();
}
3、客户端
客户端的设计用第三节中的同步模式即可,客户端不需要异步的方式,因为客户端并不是以并发为主,当然写成异步收发更好一些。
4、运行结果
5、总结
到这里依然是白雪,也是不会再实际项目中使用的,主要有以下原因
- 因为该服务器的发送和接收以应答的方式交互,而并不能做到应用层想随意发送的目的,也就是未做到完全的收发分离(全双工逻辑)。
- 该服务器未处理粘包,序列化,以及逻辑和收发线程解耦等问题。
这些问题我们会在接下来的文章中不断完善。