第一章 未使用队列直接异步发送(发送一部分)
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;
}