您好,欢迎来到华佗健康网。
搜索
您的当前位置:首页五、实现基于官方案例的异步server

五、实现基于官方案例的异步server

来源:华佗健康网

        在上一小节中,我们介绍了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];
};

         接下来我们去是实现他们

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、总结

        到这里依然是白雪,也是不会再实际项目中使用的,主要有以下原因

  • 因为该服务器的发送和接收以应答的方式交互,而并不能做到应用层想随意发送的目的,也就是未做到完全的收发分离(全双工逻辑)。
  • 该服务器未处理粘包,序列化,以及逻辑和收发线程解耦等问题。

        另外,该demo示例为仿照asio官网编写的,其中存在隐患,就是当服务器即将发送数据前(调用async_write前),此刻客户端中断,服务器此时调用async_write会触发发送回调函数,判断ec为非0进而执行delete this逻辑回收session。但要注意的是客户端关闭后,在tcp层面会触发读就绪事件,服务器会触发读事件回调函数。在读事件回调函数中判断错误码ec为非0,进而再次执行delete操作,从而造成二次析构,这是极度危险的。        

        这些问题我们会在接下来的文章中不断完善。 

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- huatuo0.com 版权所有 湘ICP备2023021991号-1

违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务