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;
}
相关推荐
北极光SD-WAN组网1 小时前
5G智慧网络如何实现异地组网?基于智能组网模块的解决方案解析
网络·5g
刘孬孬沉迷学习1 小时前
5G网络gNB与核心网(5GC)连接架构及传输协议
网络·网络协议·tcp/ip·5g·架构·udp·信息与通信
Xiaok10181 小时前
libpcap 抓包:从打开网卡到解析数据包
服务器·网络·php
爱奥尼欧1 小时前
【Linux笔记】网络部分——传输层协议TCP(1)
linux·运维·网络·笔记·tcp/ip·1024程序员节
lang201509281 小时前
WebSocket子协议STOMP
网络·websocket·网络协议
饺子大魔王的男人2 小时前
3秒传输GB级文件:FastSend让P2P共享告别云存储依赖
网络·网络协议·p2p
一叶知秋yyds3 小时前
openwrt 系统下通过命令行设置允许wan口进行Luci页面的访问
网络·openwrt·luci 开启wan 口访问
网络安全-海哥4 小时前
Web安全深度实战:从漏洞挖掘到安全防护
网络·web安全·网络安全·网络攻击·转行
我叫汪枫5 小时前
《HTTP 实战:常用调试工具与抓包技巧》
网络·网络协议·http