Boost网络库API学习笔记

第一章 未使用队列直接异步发送(发送一部分)

async_write_some一次不能全部发完;

cpp 复制代码
// 函数原型
template<typename ConstBufferSequence, typename WriteHandler>
void async_write_some(const ConstBufferSequence & buffers, WriteHandler handler);

/**********************************************/


void Session::writeCallBackErr(const boost::system::error_code& ec, std::size_t byte_transferred, std::shared_ptr<MsgNode> msg_node)
{
    if(ec.value() != 0){
        std::cout << "发送错误:" << ec.value() << " 错误的原因是:" << ec.message();
        return;
    }

    if (byte_transferred + msg_node->_cur_len
        < msg_node->_total_len) {
        _send_node->_cur_len += byte_transferred;
        this->_socket->async_write_some(
                boost::asio::buffer(_send_node->_msg + _send_node->_cur_len, _send_node->_total_len - _send_node->_cur_len),
                std::bind(&Session::writeCallBackErr, this, std::placeholders::_1, std::placeholders::_2, _send_node));
    }
}

/* 打开服务器,初次接受到一个连接,需要返回(发送)一个hello world!
* writeToSocketErr就调用回调函数发送数据,
* 但是刚刚刚发送了hello后,又来一个连接,又需要返回(发送一个hello world!);
* 这时会将第二次要发送的hello world!先发送出去,在发送world!;
* 发送顺序会是:hellohello world! world!
* 这就是异步发送的缺陷,发送的消息会乱套
*/
void Session::writeToSocketErr(const std::string buf)
{
    // 需要发送的数据对象
    _send_node = std::make_shared<MsgNode>(buf.c_str(), buf.length());

    /*
    * 异步发送数据
    * 第一个参数:需要发送的篮子
    * 第二个参数:具体使用哪个回调函数发送数据
    */

    this->_socket->async_write_some(
            boost::asio::buffer(_send_node->_msg, _send_node->_total_len),
            std::bind(&Session::writeCallBackErr, this, std::placeholders::_1, std::placeholders::_2, _send_node)
    );
}
  • async_write_some() 只会尝试写入部分数据,这意味着它并不保证发送完所有传入的缓冲区数据。你可能需要在回调函数中检查写入的字节数,并根据需要再次调用 async_write_some() 发送剩余的数据。
  • std::size_t bytes_transferred表示已经写了多少个字节的数据;

1.1 出现的问题

不使用队列,使得发送的数据会乱序;

第二章 使用队列进行异步发送(发送一部分)

cpp 复制代码
void Session::writeCallBack(const boost::system::error_code &ec, std::size_t byte_transferred) {
    if (ec.value() != 0) {
        std::cout << "Error , code is " << ec.value() << " . Message is " << ec.message();
        return;
    }

    // 取出队首元素即当前未发送完数据
    auto & send_data = _send_queue.front();
    send_data->_cur_len += byte_transferred;
    //数据未发送完, 则继续发送
    if (send_data->_cur_len < send_data->_total_len) {
        this->_socket->async_write_some(boost::asio::buffer(send_data->_msg + send_data->_cur_len, send_data->_total_len-send_data->_cur_len),
                                        std::bind(&Session::writeCallBack,
                                                  this, std::placeholders::_1, std::placeholders::_2));
        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_write_some(boost::asio::buffer(send_data->_msg + send_data->_cur_len, send_data->_total_len - send_data->_cur_len),
                                        std::bind(&Session::writeCallBack,
                                                  this, std::placeholders::_1, std::placeholders::_2));
    }


}

void Session::writeToSocket(const std::string buf) {
    // 将要发送的数据放入到发送队列中
    _send_queue.emplace(new MsgNode(buf.c_str(), buf.length()));

    // 如果发送队列中有未发送完的数据
    if(_send_pending){
        return;
    }

    // 调用异步发送数据
    this->_socket->async_write_some(
            boost::asio::buffer(_send_node->_msg, _send_node->_total_len),
            std::bind(&Session::writeCallBack, this, std::placeholders::_1, std::placeholders::_2)
    );

    // 处于真正发送状态
    _send_pending = true;
}

2.1 出现的问题

使用了队列,解决乱序问题。但是依然存在的问题是频繁的调用回调函数;

第三章 使用队列进行异步发送(一次发送全部数据)

cpp 复制代码
//不能与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(boost::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(boost::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));
    }
}

3.1 特别注意

async_send不能与async_write_some混合使用

工程中常用async_send+队列的方式

第四章 异步读取数据(一次读取部分)

cpp 复制代码
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(boost::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(boost::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;
}

4.1 出现的问题

会出现沾包问题,并且多次调用回调函数

第五章 异步读取数据(一次读取全部数据)

cpp 复制代码
void Session::readAllFromSocket(const std::string& buf) {
    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_receive(boost::asio::buffer(_recv_node->_msg, _recv_node->_total_len), std::bind(&Session::readAllCallBack, this,
                                                                                             std::placeholders::_1, std::placeholders::_2));
    _recv_pending = true;
}

void Session::readAllCallBack(const boost::system::error_code& ec, std::size_t bytes_transferred) {
    _recv_node->_cur_len += bytes_transferred;
    //将数据投递到队列里交给逻辑线程处理,此处略去
    //如果读完了则将标记置为false
    _recv_pending = false;
    //指针置空
    _recv_node = nullptr;
}

5.1 出现的问题

会出现沾包问题

工程常用async_read_some进行读操作

第六章 全部代码

  • 头文件
cpp 复制代码
#pragma once
#include <iostream>
#include <memory>
#include <boost/asio.hpp>
#include <queue>

// 需要接收的消息长度
const int RECVSIZE = 1024;

// 需要接受或者发送的消息对象体
// MsgNode是一个数据对象
class MsgNode {

public:

    // 发送节点的构造函数
    MsgNode(const char* msg, int total_len)
            :_total_len(total_len), _cur_len(0){

        // 开辟一块空间
        _msg = new char[total_len];

        // 初始化
        memset(_msg, '\0', total_len);
    }

    // 接受节点的构造函数
    MsgNode(int total_len) :_total_len(total_len), _cur_len(0) {
        // 开辟一块空间
        _msg = new char[total_len];
    }

    ~MsgNode() {
        delete[] _msg;
    }

public:
    // 消息的首地址
    char* _msg;

    // 消息的总长度
    int _total_len;

    // 已经接受到的消息长度(已经发送的消息长度)
    int _cur_len;
};

/*
* 服务器端接受到一个客户端的请求,就会创建一个socket对象去服务来请求的客户端
* 至此将服务器端的socket对象,创建为一个对象封装起来
*/
class Session
{
public:
    explicit Session(std::shared_ptr<boost::asio::ip::tcp::socket> socket);
    ~Session();

    // _socket需要连接的端点
    void Connect(const boost::asio::ip::tcp::endpoint &ep);

    /*
    * _socket的写函数的回调函数(错误示范,新手犯的错误)
    * @param ec   错误码
    * @param ec   已经发送的数据
    * std::shared_ptr<MsgNode>  引言计数对象
    */
    void writeCallBackErr(const boost::system::error_code &ec,
                          std::size_t byte_transferred,
                          std::shared_ptr<MsgNode>);


    /*
    * _socket的写函数(错误示范,新手犯的错误)
    * @param buf   需要发送的数据
    */
    void writeToSocketErr(const std::string buf);


    void writeCallBack(const boost::system::error_code &ec,
                          std::size_t byte_transferred);
    void writeToSocket(const std::string buf);

    void writeAllToSocket(const std::string& buf);
    void writeAllCallBack(const boost::system::error_code& ec, std::size_t bytes_transferred);

    void readFromSocket();
    void readCallBack(const boost::system::error_code& ec, std::size_t bytes_transferred);

    void readAllFromSocket(const std::string& buf);
    void readAllCallBack(const boost::system::error_code& ec, std::size_t bytes_transferred);


private:
    // 创建一个_socket对象
    std::shared_ptr<boost::asio::ip::tcp::socket> _socket;

    // 返回给客户端的消息
    std::shared_ptr<MsgNode> _send_node;

    // 判断当前队列是否还有数据
    bool _send_pending;

    // 存放要发送数据的队列
    std::queue<std::shared_ptr<MsgNode>> _send_queue;

    // 接收数据节点
    std::shared_ptr<MsgNode> _recv_node;

    // 表示是否正在接受
    bool _recv_pending;
};
  • 源文件
cpp 复制代码
#include "Session.h"


Session::Session(std::shared_ptr<boost::asio::ip::tcp::socket> socket)
        :_socket(socket), _send_pending(false), _recv_pending(false){

}

Session::~Session() {

}

void Session::Connect(const boost::asio::ip::tcp::endpoint& ep) {
    // 连接客户端的端点
    this->_socket->connect(ep);
}

void Session::writeCallBackErr(const boost::system::error_code& ec, std::size_t byte_transferred, std::shared_ptr<MsgNode> msg_node)
{
    if(ec.value() != 0){
        std::cout << "发送错误:" << ec.value() << " 错误的原因是:" << ec.message();
        return;
    }

    if (byte_transferred + msg_node->_cur_len
        < msg_node->_total_len) {
        _send_node->_cur_len += byte_transferred;
        this->_socket->async_write_some(
                boost::asio::buffer(_send_node->_msg + _send_node->_cur_len, _send_node->_total_len - _send_node->_cur_len),
                std::bind(&Session::writeCallBackErr, this, std::placeholders::_1, std::placeholders::_2, _send_node));
    }
}

/* 打开服务器,初次接受到一个连接,需要返回(发送)一个hello world!
* writeToSocketErr就调用回调函数发送数据,
* 但是刚刚刚发送了hello后,又来一个连接,又需要返回(发送一个hello world!);
* 这时会将第二次要发送的hello world!先发送出去,在发送world!;
* 发送顺序会是:hellohello world! world!
* 这就是异步发送的缺陷,发送的消息会乱套
*/
void Session::writeToSocketErr(const std::string buf)
{
    // 需要发送的数据对象
    _send_node = std::make_shared<MsgNode>(buf.c_str(), buf.length());

    /*
    * 异步发送数据
    * 第一个参数:需要发送的篮子
    * 第二个参数:具体使用哪个回调函数发送数据
    */

    this->_socket->async_write_some(
            boost::asio::buffer(_send_node->_msg, _send_node->_total_len),
            std::bind(&Session::writeCallBackErr, this, std::placeholders::_1, std::placeholders::_2, _send_node)
    );
}

void Session::writeCallBack(const boost::system::error_code &ec, std::size_t byte_transferred) {
    if (ec.value() != 0) {
        std::cout << "Error , code is " << ec.value() << " . Message is " << ec.message();
        return;
    }

    // 取出队首元素即当前未发送完数据
    auto & send_data = _send_queue.front();
    send_data->_cur_len += byte_transferred;
    //数据未发送完, 则继续发送
    if (send_data->_cur_len < send_data->_total_len) {
        this->_socket->async_write_some(boost::asio::buffer(send_data->_msg + send_data->_cur_len, send_data->_total_len-send_data->_cur_len),
                                        std::bind(&Session::writeCallBack,
                                                  this, std::placeholders::_1, std::placeholders::_2));
        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_write_some(boost::asio::buffer(send_data->_msg + send_data->_cur_len, send_data->_total_len - send_data->_cur_len),
                                        std::bind(&Session::writeCallBack,
                                                  this, std::placeholders::_1, std::placeholders::_2));
    }


}

void Session::writeToSocket(const std::string buf) {
    // 将要发送的数据放入到发送队列中
    _send_queue.emplace(new MsgNode(buf.c_str(), buf.length()));

    // 如果发送队列中有未发送完的数据
    if(_send_pending){
        return;
    }

    // 调用异步发送数据
    this->_socket->async_write_some(
            boost::asio::buffer(_send_node->_msg, _send_node->_total_len),
            std::bind(&Session::writeCallBack, this, std::placeholders::_1, std::placeholders::_2)
    );

    // 处于真正发送状态
    _send_pending = true;
}

//不能与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(boost::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(boost::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));
    }
}

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(boost::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(boost::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;
}

void Session::readAllFromSocket(const std::string& buf) {
    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_receive(boost::asio::buffer(_recv_node->_msg, _recv_node->_total_len), std::bind(&Session::readAllCallBack, this,
                                                                                             std::placeholders::_1, std::placeholders::_2));
    _recv_pending = true;
}

void Session::readAllCallBack(const boost::system::error_code& ec, std::size_t bytes_transferred) {
    _recv_node->_cur_len += bytes_transferred;
    //将数据投递到队列里交给逻辑线程处理,此处略去
    //如果读完了则将标记置为false
    _recv_pending = false;
    //指针置空
    _recv_node = nullptr;
}
相关推荐
爱码小白26 分钟前
网络编程(王铭东老师)笔记
服务器·网络·笔记
蜜獾云36 分钟前
linux firewalld 命令详解
linux·运维·服务器·网络·windows·网络安全·firewalld
柒烨带你飞1 小时前
路由器转发数据报的封装过程
网络·智能路由器
东方隐侠安全团队-千里2 小时前
网安瞭望台第17期:Rockstar 2FA 故障催生 FlowerStorm 钓鱼即服务扩张现象剖析
网络·chrome·web安全
神一样的老师2 小时前
面向高精度网络的时间同步安全管理架构
网络·安全·架构
与海boy3 小时前
CentOS7网络配置,解决不能联网、ping不通外网、主机的问题
linux·网络·网卡
我叫czc4 小时前
【python高级】342-TCP服务器开发流程
服务器·网络·tcp/ip
a_weng084 小时前
CS 144 check6: buiding an IP router
网络·网络协议·计算机网络
終不似少年遊*4 小时前
华为云计算HCIE笔记04
网络·华为云·云计算·学习笔记·hcie·认证·数据中心
红米饭配南瓜汤4 小时前
WebRTC服务质量(09)- Pacer机制(01) 流程概述
网络·音视频·webrtc