同步读写的优缺点
缺点:
- 读写是阻塞的,如果客户端不发送数据的话,服务器就会一直阻塞在read上,导致服务器一直处于等待状态。
- 一般是通过开辟一个新的线程来服务客户端的请求,但是一个进程可以开辟的线程数是有限的,大约为2048个,在linux环境下可以通过unlimit增加线程数,但是线程过多也会增加切换消耗的资源。
- 同步一般为应答模式,实际上我们应该将发送和接收单独分开。
优点:
- 客户端连接数不多,而且服务器并发性不高的场景,可以使用同步读写的方式。
- 使用同步读写能简化编码难度。
异步写
class Session{
public:
void WriteCallBack(const boost::system::error_code& ec, std::size_t bytes_transferred);
void WriteToSocket(const std::string &buf);
private:
std::queue<std::shared_ptr<MsgNode>> _send_queue;
std::shared_ptr<asio::ip::tcp::socket> _socket;
bool _send_pending;
};
我们通过维护一个队列和bool变量(其实用原子可能更好)来保证写的顺序性,bool变量表示当前的发送任务是否全部发送完成。
//不能与async_write_some混合使用
void Session::WriteAllToSocket(const std::string& buf) {
//插入发送队列
_send_queue.emplace(new MsgNode(buf.c_str(), buf.length()));
//pending状态说明上一次有未发送完的数据
if (_send_pending) {
return;
}
//异步发送数据,因为异步所以不会一下发送完
this->_socket->async_send(asio::buffer(buf),
std::bind(&Session::WriteAllCallBack, this,
std::placeholders::_1, std::placeholders::_2));
_send_pending = true;
}
void Session::WriteAllCallBack(const boost::system::error_code& ec, std::size_t bytes_transferred){
if (ec.value() != 0) {
std::cout << "Error occured! Error code = "
<< ec.value()
<< ". Message: " << ec.message();
return;
}
//如果发送完,则pop出队首元素
_send_queue.pop();
//如果队列为空,则说明所有数据都发送完,将pending设置为false
if (_send_queue.empty()) {
_send_pending = false;
}
//如果队列不是空,则继续将队首元素发送
if (!_send_queue.empty()) {
auto& send_data = _send_queue.front();
this->_socket->async_send(asio::buffer(send_data->_msg + send_data->_cur_len, send_data->_total_len - send_data->_cur_len),
std::bind(&Session::WriteAllCallBack,
this, std::placeholders::_1, std::placeholders::_2));
}
}
异步读
class Session {
public:
void ReadFromSocket();
void ReadCallBack(const boost::system::error_code& ec, std::size_t bytes_transferred);
private:
std::shared_ptr<asio::ip::tcp::socket> _socket;
std::shared_ptr<MsgNode> _recv_node;
bool _recv_pending;
};
_recv_node用来缓存接收的数据,_recv_pending为true表示节点正在接收数据,还未接受完。
//不考虑粘包情况, 先用固定的字节接收
void Session::ReadFromSocket() {
if (_recv_pending) {
return;
}
//可以调用构造函数直接构造,但不可用已经构造好的智能指针赋值
/*auto _recv_nodez = std::make_unique<MsgNode>(RECVSIZE);
_recv_node = _recv_nodez;*/
_recv_node = std::make_shared<MsgNode>(RECVSIZE);
_socket->async_read_some(asio::buffer(_recv_node->_msg, _recv_node->_total_len), std::bind(&Session::ReadCallBack, this,
std::placeholders::_1, std::placeholders::_2));
_recv_pending = true;
}
void Session::ReadCallBack(const boost::system::error_code& ec, std::size_t bytes_transferred){
_recv_node->_cur_len += bytes_transferred;
//没读完继续读
if (_recv_node->_cur_len < _recv_node->_total_len) {
_socket->async_read_some(asio::buffer(_recv_node->_msg+_recv_node->_cur_len,
_recv_node->_total_len - _recv_node->_cur_len), std::bind(&Session::ReadCallBack, this,
std::placeholders::_1, std::placeholders::_2));
return;
}
//将数据投递到队列里交给逻辑线程处理,此处略去
//如果读完了则将标记置为false
_recv_pending = false;
//指针置空
_recv_node = nullptr;
}
标签:asio,异步,node,读写,send,std,Session,len,recv
From: https://www.cnblogs.com/dwinternet/p/18425532