同步读写的优缺点
缺点:
- 读写是阻塞的,如果客户端不发送数据的话,服务器就会一直阻塞在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设置为falseif (_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;
}