当前位置: 首页 > news >正文

C++ asio网络编程(5)简单异步echo服务器

上一篇文章:C++ asio网络编程(4)异步读写操作及注意事项

文章目录

  • 前言
  • 一、Session类
    • 1.代码
    • 2.代码详解
    • 3.实现Session类
      • 1.构造函数
      • 2.handle_read
      • 3.介绍一下boost的封装函数和api
      • 4.handle_write
  • 二、Server类
    • 1.代码
    • 2.代码思路详解
  • 三、客户端
  • 四、运行截图与流程图


前言

提示:这里可以添加本文要记录的大概内容:

前文已经介绍了异步操作的api,今天写一个简单的异步echo服务器,以应答为主
在这里插入图片描述


提示:以下是本篇文章正文内容,下面案例可供参考

一、Session类

Session类主要是处理客户端消息收发的会话类,为了简单起见,我们不考虑粘包问题,也不考虑支持手动调用发送的接口只以应答的方式发送和接收固定长度(1024字节长度)的数据。

1.代码

#pragma once
#include<iostream>
#include<boost/asio.hpp>
using boost::asio::ip::tcp;class Session
{Session(boost::asio::io_context& ioc) :_socket(ioc){};tcp::socket& Socket(){return _socket;}void Start();//监听客户端的读写private://回调函数void handle_read(const boost::system::error_code& ec,size_t bytes_transferred);void handle_write(const boost::system::error_code& ec, size_t bytes_transferred);tcp::socket _socket;enum {max_length=1024};char _data[max_length];
};

2.代码详解

1.构造函数中传入一个上下文用来绑定socket
2.Start()用来监听客户端是否发来信息
3.回调函数中 handle_read 是读的回调函数,我们这里用上一个文章介绍的async_read_some
因为不会一次性读完,所以我们有第二个参数来显示已经读了多少数据
4.回调函数中的handle_write是写的回调函数,我们用boost::asio::async_write来一次性发送数据
5. _data用来接收客户端传递的数据
6. _socket为单独处理客户端读写的socket

3.实现Session类

1.构造函数

我们在构造函数中把数组初始化为0,然后直接调用读函数就行,他会一直等着,当用户发送数据,tcp底层有内容的时候,就会进行读

void Session::Start()
{memset(_data, 0, sizeof(_data));_socket.async_read_some(boost::asio::buffer(_data, max_length),std::bind(&Session::handle_read, this, placeholders::_1, placeholders::_2));
}

然后读了以后,我们要进行回调函数
这个回调函数的参数是两个的原因是因为源码中async_read_some要求了回调函数参数如下
在这里插入图片描述

2.handle_read

//读的回调函数
void Session::handle_read(const boost::system::error_code& ec, size_t bytes_transferred)
{if (ec)//0正确  1错误{cout << "read error" << endl;delete this;}else{cout << "server receivr data is "<<_data << endl;//将收到的数据发送回去boost::asio::async_write(_socket, boost::asio::buffer(_data, bytes_transferred),std::bind(&Session::handle_write,this, placeholders::_1, placeholders::_2));}
}

读完以后我们打印一下收到的数据
然后原封不动的将这个数据发送回去就行

我们用boost::asio::async_write来发送,这样可以保证数据一次性发送完,但其实用socket自带也都行,因为我们这样只做一个简单的应答式的而已,本身就不会出现长数据,只是一个模型而已,但这里用一个boost库自带的封装函数,就是为了介绍一下!!!!!!!!!!!!!!!!!!!!!

3.介绍一下boost的封装函数和api

在这里插入图片描述
在这里插入图片描述

参数列表有三个,第一个绑定socket,第二个传入的buffer,第三个std::bind

4.handle_write

在之前读完以后我们就准备将数据发送回去,所以就写,写的时候也调用我们这个回调函数,参数的原因与上面同理,查看源码即可

//写的回调函数
void Session::handle_write(const boost::system::error_code& ec, size_t bytes_transferred)
{ if (ec)//0正确  1错误{cout << "write error" << endl;delete this;}else{//发完了 就清除掉原先的memset(_data, 0, sizeof(_data));//继续读_socket.async_read_some(boost::asio::buffer(_data, bytes_transferred),std::bind(&Session::handle_read, this, placeholders::_1, placeholders::_2));}
}

二、Server类

这个用来管理客户端连接的,我们初始化一个监听器,当有客户端进行连接的时候,我们就内部new一个Session去单独服务这个客户端

1.代码

class Server
{
public:Server(boost::asio::io_context& ioc, short port);private:void start_accept();//监听连接void handle_accept(Session* new_session, const boost::system::error_code& ec);//当有连接的时候触发这个回调函数boost::asio::io_context& _ioc;//因为上下文不允许被复制 所以用引用tcp::acceptor _acceptor;
};
Server::Server(boost::asio::io_context& ioc, short port) :_ioc(ioc), _acceptor(ioc, tcp::endpoint(tcp::v4(), port))
{start_accept();
}void Server::start_accept()
{Session* new_session = new Session(_ioc);_acceptor.async_accept(new_session->Socket(),std::bind(&Server::handle_accept,this,new_session,placeholders::_1));
}void Server::handle_accept(Session* new_session, const boost::system::error_code& ec)
{if (!ec)//成功{new_session->Start();}else{delete new_session;}start_accept();//再次准备
}

2.代码思路详解

首先我们初始化一个上下文ioc准备好,因为创建一个socket连接需要这个东西
然后我们调用这个函数start_accept()
这个函数内部会创建一个Session准备好,相当于我提前准备一个服务员准备好
然后当监听器 _acceptor 被触发的时候,我们就调用回调函数(相当于一个房间),让之前准备好的服务员去服务这个客人

_acceptor.async_accept(new_session->Socket(),std::bind(&Server::handle_accept,this,new_session,placeholders::_1));

但是这里要提前绑定一个new_session这个参数,因为根据源码,回调函数的参数只能有一个,就是错误码

在这里插入图片描述
然后进入handle_accept函数,如果没有错误我们就调用这个创建的session去进行Start()
在Start中就会进行读写循环调用

三、客户端

客户端这么我们就不进行说明,因为今天做的是简单的异步echo模型,应答式的,并且客户端我们在第二节文章讲过了,用的客户端代码也是第二个文章的代码,所以就不进行说明

#include<boost/asio.hpp>
#include <iostream>
const int MAX_LENGTH = 1024;//表示发送和接收最大长度为1024字节
int main()
{while (1){try{//创建上下文服务boost::asio::io_context ios;//创建endpointboost::asio::ip::tcp::endpointremote_ep(boost::asio::ip::make_address("127.0.0.1"), 10086);//127.0.0.1是本机的回路地址 因为客户端和服务端在同一地址//服务器就是本机boost::asio::ip::tcp::socket sock(ios, remote_ep.protocol());boost::system::error_code error = boost::asio::error::host_not_found;sock.connect(remote_ep, error);if (error){std::cout << "connect failed,code is:" << error.value() <<"error message is" << error.message() << std::endl;return 0;}std::cout << "请输入需要发送的信息:";char request[MAX_LENGTH];std::cin.getline(request, MAX_LENGTH);size_t request_length = strlen(request);//获取实际要发送的数据长度//发送数据boost::asio::write(sock, boost::asio::buffer(request, request_length));char reply[MAX_LENGTH];//存储接收数据//用read读size_t reply_length = 0;boost::system::error_code ec;reply_length = sock.read_some(boost::asio::buffer(reply, MAX_LENGTH), ec);if (ec){std::cerr << "Read failed: " << ec.message() << std::endl;return 0;}std::cout << "回复:";std::cout.write(reply, reply_length);std::cout << "\n";}catch (std::exception& e){std::cerr << "Exception is: " << e.what() << std::endl;}}return 0;
}

四、运行截图与流程图

在这里插入图片描述
服务端接收完直接原文回给客户端就行
这里服务端报错 read error是因为客户端每发送一次就会断开连接,这是我们之前代码设定的

在这里插入图片描述

相关文章:

  • 【】东方财务的Choice数据量化接口,在linux上安装python 版本,需要联系客户经理审核通过后就可以使用了。使用接口更加稳定和全面。
  • 智能指针入门:深入理解 C++ 的 shared_ptr
  • 【Mysql基础】一、基础入门和常见SQL语句
  • Matlab自学笔记五十四:符号数学工具箱和符号运算、符号求解、绘图
  • LLaMA Factory 深度调参
  • 右值和移动
  • 国产化Excel处理控件Spire.XLS系列教程:如何通过 C# 删除 Excel 工作表中的筛选器
  • 开疆智能Profinet转Canopen网关连接sick RFID读写器配置案例
  • 212. 单词搜索 II【 力扣(LeetCode) 】
  • RDB和AOF的区别
  • Kubernetes vs. OpenShift:深入比较与架构解析
  • Java学习手册:客户端负载均衡
  • Matlab 模糊pid的液压舵机伺服系统
  • 基于微信小程序的城市特色旅游推荐应用的设计与实现
  • Milvus 2.4 使用详解:从零构建向量数据库并实现搜索功能(Python 实战)
  • 记一次redis未授权被种挖矿
  • Java中进阶并发编程
  • langchain4j中使用milvus向量数据库做RAG增加索引
  • 新能源汽车电池加热技术:传统膜加热 vs. 脉冲自加热
  • C++类成员
  • 2025上海科技节本周六启幕,机器人和科学家同走AI科学红毯
  • 牧原股份子公司与养殖户种猪买卖纠纷案一审胜诉
  • 重庆荣昌区委区政府再设“答谢宴”,邀请800余名志愿者机关食堂用餐
  • 习近平结束对俄罗斯国事访问并出席纪念苏联伟大卫国战争胜利80周年庆典回到北京
  • 马上评丨全民定制公交,打开城市出行想象空间
  • 习近平会见塞尔维亚总统武契奇