Boost.Asio 异步读写操作

Boost.Asio 异步读写操作


参考资料:
asio异步读写操作及注意事项

目录
  1. 异步写操作
    • async_write_some
    • async_send
    • 基于 async_write_some 的封装
    • 基于 async_send 的封装
  2. 异步读操作
    • async_read_some
    • async_receive
    • 基于 async_read_some 的封装
    • 基于 async_receive 的封装
  3. 实际应用场景:实现一个简单的网络通信服务
    • 服务端示例
    • 客户端示例

1. 异步写操作

1.1 async_write_some
  • 特点
    • 每次只尽可能多地发送数据,实际发送的字节数可能小于请求发送的总字节数。
    • 应用层需通过回调函数检查已发送字节数,并循环调用直到数据完全发送。
  • 适用场景
    • 更精细的控制发送过程,例如流式传输或对发送进度有特殊要求。
1.2 async_send
  • 特点
    • 内部会循环调用 async_write_some,直到数据完全发送。
    • 回调函数只在发送完成或发生错误时触发。
  • 适用场景
    • 需要简化发送逻辑时使用。

1.3 基于 async_write_some 的封装

实现逻辑

  • 使用队列管理待发送数据,保证发送顺序。
  • 在回调函数中检查发送进度,如果未发送完成则继续发送。

代码

void Session::WriteToSocket(const std::string& buf) {
    _send_queue.emplace(std::make_shared<MsgNode>(buf.c_str(), buf.length()));
    if (_send_pending) return;

    _send_pending = true;
    auto front = _send_queue.front();
    _socket->async_write_some(
        asio::buffer(front->_msg, front->_total_len),
        [this, front](const boost::system::error_code& ec, std::size_t bytes_transferred) {
            this->HandleWrite(ec, bytes_transferred);
        });
}

void Session::HandleWrite(const boost::system::error_code& ec, std::size_t bytes_transferred) {
    if (ec) {
        std::cerr << "Error during async write: " << ec.message() << std::endl;
        return;
    }

    auto front = _send_queue.front();
    front->_cur_len += bytes_transferred;

    if (front->_cur_len < front->_total_len) {
        _socket->async_write_some(
            asio::buffer(front->_msg + front->_cur_len, front->_total_len - front->_cur_len),
            [this, front](const boost::system::error_code& ec, std::size_t bytes_transferred) {
                this->HandleWrite(ec, bytes_transferred);
            });
    } else {
        _send_queue.pop();
        if (_send_queue.empty()) {
            _send_pending = false;
        } else {
            WriteToSocket("");  // 继续发送队列中的下一个消息
        }
    }
}

1.4 基于 async_send 的封装

实现逻辑

  • 使用队列管理数据,依次调用 async_send,简化发送逻辑。

代码

void Session::WriteAllToSocket(const std::string& buf) {
    _send_queue.emplace(std::make_shared<MsgNode>(buf.c_str(), buf.length()));
    if (_send_pending) return;

    _send_pending = true;
    auto front = _send_queue.front();
    _socket->async_send(
        asio::buffer(front->_msg, front->_total_len),
        [this, front](const boost::system::error_code& ec, std::size_t bytes_transferred) {
            this->WriteAllCallBack(ec, bytes_transferred);
        });
}

void Session::WriteAllCallBack(const boost::system::error_code& ec, std::size_t bytes_transferred) {
    if (ec) {
        std::cerr << "Error during async send: " << ec.message() << std::endl;
        return;
    }

    _send_queue.pop();
    if (_send_queue.empty()) {
        _send_pending = false;
    } else {
        WriteAllToSocket("");  // 继续发送队列中的下一个消息
    }
}

2. 异步读操作

2.1 async_read_some
  • 特点
    • 每次只尽可能多地读取数据,实际读取的字节数可能小于请求的总字节数。
    • 需要在回调函数中检查已读取字节数,并循环调用直到数据完全读取。
  • 适用场景
    • 需要精细控制读取过程的场景。
2.2 async_receive
  • 特点
    • 内部会循环调用 async_read_some,直到数据完全读取。
    • 回调函数只在读取完成或发生错误时触发。
  • 适用场景
    • 需要简化读取逻辑时使用。

2.3 基于 async_read_some 的封装

实现逻辑

  • 初始化接收缓冲区,调用 async_read_some 开始接收数据。
  • 在回调函数中检查读取进度,循环调用直到读取完成。

代码

void Session::ReadFromSocket() {
    if (_recv_pending) return;

    _recv_node = std::make_shared<MsgNode>(RECVSIZE);
    _socket->async_read_some(
        asio::buffer(_recv_node->_msg, _recv_node->_total_len),
        [this](const boost::system::error_code& ec, std::size_t bytes_transferred) {
            this->ReadCallBack(ec, bytes_transferred);
        });
    _recv_pending = true;
}

void Session::ReadCallBack(const boost::system::error_code& ec, std::size_t bytes_transferred) {
    if (ec) {
        std::cerr << "Error during async read: " << ec.message() << std::endl;
        return;
    }

    _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),
            [this](const boost::system::error_code& ec, std::size_t bytes_transferred) {
                this->ReadCallBack(ec, bytes_transferred);
            });
    } else {
        // 数据读取完成
        _recv_pending = false;
        _recv_node = nullptr;
    }
}

2.4 基于 async_receive 的封装

实现逻辑

  • 初始化接收缓冲区,调用 async_receive 一次性读取数据。

代码

void Session::ReadAllFromSocket() {
    if (_recv_pending) return;

    _recv_node = std::make_shared<MsgNode>(RECVSIZE);
    _socket->async_receive(
        asio::buffer(_recv_node->_msg, _recv_node->_total_len),
        [this](const boost::system::error_code& ec, std::size_t bytes_transferred) {
            this->ReadAllCallBack(ec, bytes_transferred);
        });
    _recv_pending = true;
}

void Session::ReadAllCallBack(const boost::system::error_code& ec, std::size_t bytes_transferred) {
    if (ec) {
        std::cerr << "Error during async receive: " << ec.message() << std::endl;
        return;
    }

    _recv_node->_cur_len += bytes_transferred;

    // 数据读取完成
    _recv_pending = false;
    _recv_node = nullptr;
}

3. 实际应用场景:网络通信服务

3.1 服务端示例

逻辑

  • 接收客户端连接。
  • 使用封装的异步读写函数进行数据收发。

代码

void StartServer(asio::io_context& io_context, short port) {
    auto acceptor = std::make_shared<asio::ip::tcp::acceptor>(io_context, asio::ip::tcp::endpoint(asio::ip::tcp::v4(), port));

    auto socket = std::make_shared<asio::ip::tcp::socket>(io_context);
    acceptor->async_accept(*socket, [socket](const boost::system::error_code& ec) {
        if (!ec) {
            auto session = std::make_shared<Session>(socket);
            session->ReadFromSocket();  // 开始接收数据
        }
    });

    io_context.run();
}

3.2 客户端示例

逻辑

  • 连接服务端。
  • 使用封装的异步读写函数与服务端通信。

代码

void StartClient(asio::io_context& io_context, const std::string& host, short port) {
    auto socket = std::make_shared<asio::ip::tcp::socket>(io_context);
    asio::ip::tcp::resolver resolver(io_context);
    asio::connect(*socket, resolver.resolve(host, std::to_string(port)));

    auto session = std::make_shared<Session>(socket);
    session->WriteToSocket("Hello, Server!");
    session->ReadFromSocket();  // 等待服务端响应

    io_context.run();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值