系列文章目录:C++ asio网络编程-CSDN博客
在全双工通信方式中,服务器会一直监听写事件,接收对端数据,也可以随时发送数据给对端,所以保证数据有序尤为重要。在这一节中,我们将会封装一个数据队列,保证数据有序。
1、封装数据结点
class MsgNode
{
friend class Session;
public:
MsgNode(char* msg, int max_len) {
_data = new char[max_len];
memcpy(_data, msg, max_len);
}
~MsgNode() {
delete[] _data;
}
private:
int _cur_len; // 数据当前已处理的长度
int _max_len; // 数据总长度
char* _data; // 数据
};
2、发送接口设计
在session类中,实现公有的send方法,用来发送数据,在设置两个私有成员变量,分别为要发送的数据队列、互斥锁,因为要保证线程安全,所以在操作数据队列时要加锁。
class Session : public std::enable_shared_from_this<Session>
{
public:
Session(boost::asio::io_context& ioc, Server* server);
boost::asio::ip::tcp::socket& getSocket();
void start();
void send(char* msg, int max_length);
std::string& getUuid();
private:
void handle_read(const boost::system::error_code& ec,
std::size_t bytes_transferred,
std::shared_ptr<Session> self_share);
void handle_write(const boost::system::error_code& ec,
std::shared_ptr<Session> self_share);
boost::asio::ip::tcp::socket _socket;
Server* _server;
std::string _uuid;
enum {MAX_LENGTH = 1024};
char _data[MAX_LENGTH];
std::queue<std::shared_ptr<MsgNode>> _send_que;
std::mutex _send_lock;
};
实现send方法
void Session::send(char* msg, int max_length)
{
bool pending = false;
std::lock_guard<std::mutex> lock(_send_lock);
if (_send_que.size() > 0) {
pending = true;
}
_send_que.push(std::make_shared<MsgNode>(msg, max_length));
if (pending) {
return;
}
boost::asio::async_write(_socket, boost::asio::buffer(msg, max_length),
std::bind(&Session::handle_write, this, std::placeholders::_1, shared_from_this()));
}
修改写回调函数
void Session::handle_write(const boost::system::error_code& ec,
std::shared_ptr<Session> self_share)
{
if (!ec) {
std::lock_guard<std::mutex> lock(_send_lock);
_send_que.pop();
if (!_send_que.empty()) {
auto& msgNode = _send_que.front();
boost::asio::async_write(_socket, boost::asio::buffer(msgNode->_data, msgNode->_max_len),
std::bind(&Session::handle_write, this, std::placeholders::_1, self_share));
}
}
else {
std::cout << "write error: " << ec.value() << std::endl;
// delete this;
_server->clearSession(_uuid);
}
}
读回调函数也可以修改一下
void Session::handle_read(const boost::system::error_code& ec,
std::size_t bytes_transferred, std::shared_ptr<Session> self_share)
{
if (!ec) {
std::cout << "server receive data is " << _data << std::endl;
send(_data, bytes_transferred);
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, self_share));
}
else {
std::cout << "read error" << std::endl;
// delete this;
_server->clearSession(_uuid);
}
}
3、总结
这就是一个全双工通信的基本逻辑,大家可以用之前的同步读写客户端进行运行测试。但是写到这里仍然存在大量缺陷,下一节中将解决粘包问题。