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

news2025/5/13 12:28:37

上一篇文章: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;
            //创建endpoint
            boost::asio::ip::tcp::endpoint
                remote_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是因为客户端每发送一次就会断开连接,这是我们之前代码设定的

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2374654.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【机器人】复现 UniGoal 具身导航 | 通用零样本目标导航 CVPR 2025

UniGoal的提出了一个通用的零样本目标导航框架&#xff0c;能够统一处理多种类型的导航任务。 支持 对象类别导航、实例图像目标导航和文本目标导航&#xff0c;而无需针对特定任务进行训练或微调。 本文分享UniGoal复现和模型推理的过程&#xff5e; 查找沙发&#xff0c;模…

spring中的@PropertySource注解详解

一、核心功能与作用 PropertySource是Spring框架中用于加载外部配置文件的核心注解&#xff0c;主要作用是将属性文件&#xff08;如.properties、.yml&#xff09;的键值对加载到Spring的Environment环境中&#xff0c;实现配置与代码的解耦。其核心价值包括&#xff1a; 外部…

二极管钳位电路——Multisim电路仿真

目录 二极管钳位电路 2.1 二极管正向钳位电路 二极管压降测试 2.1.1 二极管正向钳位电路图 2.1.2 二极管正向钳位工作原理 2.2 二极管负向钳位电路 2.2.1 二极管负向钳位电路图 2.2.2 二极管负向钳位工作原理 二极管正向反向钳位仿真电路实验结果 2.3 二极管顶部钳位…

suricata增加单元测试编译失败

一、环境 $ lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 22.04.5 LTS Release: 22.04 Codename: jammysuricata: suricata7.0.5 IDE: vscode 二、背景 在suricata中开发了某个功能后&#xff0c;增加unittest时&#xff0c;…

高并发场景下的BI架构设计:衡石分布式查询引擎与缓存分级策略

在电商大促、金融交易时段或IoT实时监控场景中&#xff0c;企业BI系统常面临瞬时万级并发查询的冲击——运营团队需要实时追踪GMV波动&#xff0c;风控部门需秒级响应欺诈检测&#xff0c;产线监控需毫秒级反馈设备状态。传统单体架构的BI系统在此类场景下极易崩溃&#xff0c;…

鱼眼摄像头(一)多平面格式 单缓冲读取图像并显示

鱼眼摄像头&#xff08;一&#xff09;多平面格式 单缓冲读取图像并显示 1.摄像头格式 1. 单平面格式&#xff08;Single Plane&#xff09;&#xff1a;各通道数据保存在同一个平面&#xff08;缓冲&#xff09;&#xff0c;图像数据按行连续存储a. mjpeg&#xff0c;yuyv等…

机器学习笔记——特征工程

大家好&#xff0c;这里是好评笔记&#xff0c;公主号&#xff1a;Goodnote&#xff0c;专栏文章私信限时Free。本笔记介绍机器学习中常见的特征工程方法、正则化方法和简要介绍强化学习。 文章目录 特征工程&#xff08;Fzeature Engineering&#xff09;1. 特征提取&#xff…

A Survey of Learning from Rewards:从训练到应用的全面剖析

A Survey of Learning from Rewards&#xff1a;从训练到应用的全面剖析 你知道大语言模型&#xff08;LLMs&#xff09;如何通过奖励学习变得更智能吗&#xff1f;这篇论文将带你深入探索。从克服预训练局限的新范式&#xff0c;到训练、推理各阶段的策略&#xff0c;再到广泛…

Python爬虫第20节-使用 Selenium 爬取小米商城空调商品

目录 前言 一、 本文目标 二、环境准备 2.1 安装依赖 2.2 配置 ChromeDriver 三、小米商城页面结构分析 3.1 商品列表结构 3.2 分页结构 四、Selenium 自动化爬虫实现 4.1 脚本整体结构 4.2 代码实现 五、关键技术详解 5.1 Selenium 启动与配置 5.2 页面等待与异…

Aware和InitializingBean接口以及@Autowired注解失效分析

Aware 接口用于注入一些与容器相关信息&#xff0c;例如&#xff1a; ​ a. BeanNameAware 注入 Bean 的名字 ​ b. BeanFactoryAware 注入 BeanFactory 容器 ​ c. ApplicationContextAware 注入 ApplicationContext 容器 ​ d. EmbeddedValueResolverAware 注入 解析器&a…

Unity3D仿星露谷物语开发41之创建池管理器

1、目标 在PersistentScene中创建池管理器&#xff08;Pool Manager&#xff09;。这将允许一个预制对象池被创建和重用。 在游戏中当鼠标点击地面时&#xff0c;便会启用某一个对象。比如点击地面&#xff0c;就创建了一棵树&#xff0c;而这棵树是从预制体对象池中获取的&a…

Modbus协议介绍

Modbus是一种串行通信协议&#xff0c;由Modicon公司&#xff08;现为施耐德电气&#xff09;在1979年为可编程逻辑控制器&#xff08;PLC&#xff09;通信而开发。它是工业自动化领域最常用的通信协议之一&#xff0c;具有开放性、简单性和跨平台兼容性&#xff0c;广泛应用于…

I/O多路复用(select/poll/epoll)

通过一个进程来维护多个Socket&#xff0c;也就是I/O多路复用&#xff0c;是一种常见的并发编程技术&#xff0c;它允许单个线程或进程同时监视多个输入/输出&#xff08;I/O&#xff09;流&#xff08;例如网络连接、文件描述符&#xff09;。当任何一个I/O流准备好进行读写操…

Westlake-Omni 情感端音频生成式输出模型

简述 github地址在 GitHub - xinchen-ai/Westlake-OmniContribute to xinchen-ai/Westlake-Omni development by creating an account on GitHub.https://github.com/xinchen-ai/Westlake-Omni Westlake-Omni 是由西湖心辰&#xff08;xinchen-ai&#xff09;开发的一个开源…

随手记录5

一些顶级思维&#xff1a; ​ 顶级思维 1、永远不要自卑。 也永远不要感觉自己比别人差&#xff0c;这个人有没有钱&#xff0c;有多少钱&#xff0c;其实跟你都没有关系。有很多人就是那个奴性太强&#xff0c;看到比自己优秀的人&#xff0c;甚至一些装逼的人&#xff0c;这…

Linux驱动:驱动编译流程了解

要求 1、开发板中的linux的zImage必须是自己编译的 2、内核源码树,其实就是一个经过了配置编译之后的内核源码。 3、nfs挂载的rootfs,主机ubuntu中必须搭建一个nfs服务器。 内核源码树 解压 tar -jxvf x210kernel.tar.bz2 编译 make x210ii_qt_defconfigmakeCan’t use ‘…

使用 Flowise 构建基于私有知识库的智能客服 Agent(图文教程)

使用 Flowise 构建基于私有知识库的智能客服 Agent(图文教程) 在构建 AI 客服时,常见的需求是让机器人基于企业自身的知识文档,提供准确可靠的答案。本文将手把手教你如何使用 Flowise + 向量数据库(如 Pinecone),构建一个结合 RAG(Retrieval-Augmented Generation)检…

RabbitMQ ③-Spring使用RabbitMQ

Spring使用RabbitMQ 创建 Spring 项目后&#xff0c;引入依赖&#xff1a; <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-amqp --> <dependency><groupId>org.springframework.boot</groupId><artifac…

linux中常用的命令(四)

目录 1-cat查看文件内容 2-more命令 3-less命令 4-head命令 5-tail命令 1-cat查看文件内容 cat中的一些操作 -b : 列出行号&#xff08;不含空白行&#xff09;-E : 将结尾的断行以 $ 的形式展示出来-n : 列出行号&#xff08;含空白行&#xff09;-T : 将 tab 键 以 ^I 显示…

利用SSRF击穿内网!kali靶机实验

目录 1. 靶场拓扑图 2. 判断SSRF的存在 3. SSRF获取本地信息 3.1. SSRF常用协议 3.2. 使用file协议 4. 172.150.23.1/24探测端口 5. 172.150.23.22 - 代码注入 6. 172.150.23.23 SQL注入 7. 172.150.23.24 命令执行 7.1. 实验步骤 8. 172.150.23.27:6379 Redis未授权…