文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

【boost网络库从青铜到王者】第七篇:asio网络编程中的异步echo服务器,以应答为主

2023-10-11 13:26

关注

文章目录

1、简介

前文已经介绍了异步操作的api,今天写一个简单的异步echo服务器,以应答为主。

2、echo模式应答异步服务器

2.1、Session会话类

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

“session.h” :

#pragma once#include#include#includeclass Session{public:Session(boost::asio::io_context& ioc);public:boost::asio::ip::tcp::socket& GetSocket();void Start();protected://接收数据回调 tcp接收缓冲区有数据void HandlerRead(const boost::system::error_code& err, size_t bytes_transferred);//发送数据回调 tcp发送缓冲区有空闲空间,就会从用户缓冲区拷贝到tcp发送缓冲区,然后发送数据void HandleSend(const boost::system::error_code& err);protected:enum {max_length = 1024};//数组接收数据char data_[max_length];private:boost::asio::ip::tcp::socket socket_;};

这段代码是一个使用Boost.Asio库实现的 C++ 类定义,用于网络通信会话。您解释这段代码:

#pragma once#include#include#include
class Session{public:    Session(boost::asio::io_context& ioc);public:    boost::asio::ip::tcp::socket& GetSocket();    void Start();protected:    // ...private:    // ...};
protected:    void HandlerRead(const boost::system::error_code& err, size_t bytes_transferred);    void HandleSend(const boost::system::error_code& err);
private:    boost::asio::ip::tcp::socket socket_;

这个私有成员变量 socket_TCP套接字的实例,用于会话内的网络通信。

enum {    max_length = 1024};

这定义了一个名为 max_length 的常量,其值为1024。这很可能是用于接收数据的缓冲区的最大长度。

总的来说,这段代码是一个使用Boost.Asio库的网络会话类的基本轮廓。它提供了管理网络通信所需的结构和组件,但没有在此片段中提供会话行为的实际实现。

“session.cpp” :

#include "Session.h"Session::Session(boost::asio::io_context& io_context):socket_(io_context){memset(data_, 0, sizeof(data_));}boost::asio::ip::tcp::socket& Session::GetSocket() {return socket_;}void Session::Start() {memset(data_, 0, max_length);socket_.async_read_some(boost::asio::buffer(data_, max_length),std::bind(&Session::HandlerRead, this, std::placeholders::_1, std::placeholders::_2));}void Session::HandlerRead(const boost::system::error_code& err, std::size_t bytes_transferred) {if (0 != err.value()) {std::cout << "read data failed!err_code is: " << err.value() << " .message: " << err.what() << std::endl;delete this;}else {std::cout << "receive data is: " << data_ << std::endl;//大部分服务器这样设计全双工通信memset(data_, 0, sizeof(data_));//继续让接收/读数据监听,这样就会造成删除bugsocket_.async_read_some(boost::asio::buffer(data_, sizeof(data_)), std::bind(&Session::HandlerRead, this, std::placeholders::_1, std::placeholders::_2));socket_.async_send(boost::asio::buffer(data_, max_length),std::bind(&Session::HandleSend, this, std::placeholders::_1));}}void Session::HandleSend(const boost::system::error_code& err) {if (0 != err.value()) {std::cout << "send data failed!err code is: " << err.value() << " .message: " << err.what() << std::endl;}else {memset(data_, 0, sizeof(data_));//继续让接收/读数据监听socket_.async_read_some(boost::asio::buffer(data_, sizeof(data_)), std::bind(&Session::HandlerRead, this, std::placeholders::_1, std::placeholders::_2));}}

这是与之前提供的 Session 类相关的实现文件。解释这段代码:

Session::Session(boost::asio::io_context& io_context)    : socket_(io_context){    memset(data_, 0, sizeof(data_));}
boost::asio::ip::tcp::socket& Session::GetSocket() {    return socket_;}
void Session::Start() {    memset(data_, 0, max_length);    socket_.async_read_some(boost::asio::buffer(data_, max_length),        std::bind(&Session::HandlerRead, this, std::placeholders::_1, std::placeholders::_2));}
void Session::HandlerRead(const boost::system::error_code& err, std::size_t bytes_transferred) {    if (0 != err.value()) {        std::cout << "read data failed! err_code is: " << err.value() << " . message: " << err.what() << std::endl;        delete this;    }    else {        std::cout << "receive data is: " << data_ << std::endl;        // 大部分服务器这样设计全双工通信        memset(data_, 0, sizeof(data_));        // 继续让接收/读数据监听, 这样就会造成删除bug        socket_.async_read_some(boost::asio::buffer(data_, sizeof(data_)),            std::bind(&Session::HandlerRead, this, std::placeholders::_1, std::placeholders::_2));        socket_.async_send(boost::asio::buffer(data_, max_length),            std::bind(&Session::HandleSend, this, std::placeholders::_1));    }}
void Session::HandleSend(const boost::system::error_code& err) {    if (0 != err.value()) {        std::cout << "send data failed! err code is: " << err.value() << " . message: " << err.what() << std::endl;        delete this;    }    else {        memset(data_, 0, sizeof(data_));        // 继续让接收/读数据监听        socket_.async_read_some(boost::asio::buffer(data_, sizeof(data_)),            std::bind(&Session::HandlerRead, this, std::placeholders::_1, std::placeholders::_2));    }}

总的来说,这段代码是 Session 类的实现,它处理了异步的数据读取和发送操作,并提供了一些错误处理和数据清理的逻辑。这是一个基本的网络会话类的实现,用于处理网络通信。

2.2、Server类为服务器接收连接的管理类

Server类为服务器接收连接的管理类。

“server.h":

#pragma once#include#include#include"Session.h"class Server{public:Server(boost::asio::io_context& io_context, int16_t port);private:void StartAccept();void HandleAccept(Session* session, const boost::system::error_code& err);private:boost::asio::io_context& io_context_;boost::asio::ip::tcp::acceptor acceptor_;};

这段代码定义了一个名为 ServerC++类,该类似乎用于创建和管理一个基于Boost.Asio库的TCP服务器。以下是对这段代码的解释:

#pragma once#include #include #include "Session.h"
class Server{public:    Server(boost::asio::io_context& io_context, int16_t port);private:    void StartAccept();    void HandleAccept(Session* session, const boost::system::error_code& err);private:    boost::asio::io_context& io_context_;    boost::asio::ip::tcp::acceptor acceptor_;};
Server::Server(boost::asio::io_context& io_context, int16_t port)    : io_context_(io_context), acceptor_(io_context, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)){    StartAccept();}
void Server::StartAccept() {    Session* new_session = new Session(io_context_);    acceptor_.async_accept(new_session->GetSocket(),        std::bind(&Server::HandleAccept, this, new_session, std::placeholders::_1));}
void Server::HandleAccept(Session* session, const boost::system::error_code& err) {    if (!err) {        session->Start();    } else {        delete session;    }    StartAccept(); // 继续等待下一个连接}

总的来说,这段代码定义了一个基于Boost.Asio的TCP服务器类 Server,该类在构造函数中初始化了必要的成员变量并开始接受传入的连接。它使用了异步操作来处理连接请求,并在每次连接接受后启动一个新的会话(Session)。

“server.cpp":

#include"server.h"Server::Server(boost::asio::io_context& io_context,int16_t port):io_context_(io_context),acceptor_(io_context,boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(),port)){StartAccept();}void Server::StartAccept() {Session* new_session = new Session(io_context_);acceptor_.async_accept(new_session->GetSocket(),std::bind(&Server::HandleAccept, this, new_session, std::placeholders::_1));}void Server::HandleAccept(Session* new_session, const boost::system::error_code& err){if (err.value() != 0){std::cout << "acceptor session failed.error_code is: " << err.value() << " .message: " << err.what() << std::endl;delete new_session;}else {std::cout << "accept new session success!" << std::endl;std::cout << "client connect,the ip:" << new_session->GetSocket().remote_endpoint().address() << std::endl;new_session->Start();}//继续监听新的客户端连接StartAccept();}

这段代码是C++server.cpp 文件,它实现了一个基于Boost.Asio库的TCP服务器类 Server 的成员函数。下面是对代码的详细解释:

Server::Server(boost::asio::io_context& io_context, int16_t port)    : io_context_(io_context)    , acceptor_(io_context, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)){    StartAccept();}
void Server::StartAccept() {    Session* new_session = new Session(io_context_);    acceptor_.async_accept(new_session->GetSocket(),        std::bind(&Server::HandleAccept, this, new_session, std::placeholders::_1));}
void Server::HandleAccept(Session* new_session, const boost::system::error_code& err){    if (err.value() != 0)    {        std::cout << "acceptor session failed. error_code is: " << err.value() << " . message: " << err.what() << std::endl;        delete new_session;    }    else {        std::cout << "accept new session success!" << std::endl;        std::cout << "client connect, the IP: " << new_session->GetSocket().remote_endpoint().address() << std::endl;        new_session->Start();    }    // 继续监听新的客户端连接    StartAccept();}

总的来说,这段代码是 Server 类的成员函数的实现,它用于接受客户端的连接请求,并在接受连接后启动会话。这是一个基本的TCP服务器的一部分,用于处理传入的连接请求。

“main.cpp”:

#include"server.h"int main() {try {boost::asio::io_context io_context;Server server(io_context, 9273);io_context.run();}catch (std::exception& e) {std::cout << "exception: " << e.what() << std::endl;}return 0;}

这段代码是一个C++程序的 main 函数,它创建了一个基于Boost.Asio库的TCP服务器并运行它。以下是对代码的详细解释:

#include "server.h"
int main() {    try {        boost::asio::io_context io_context;        Server server(io_context, 9273);        io_context.run();    }    catch (std::exception& e) {        std::cout << "exception: " << e.what() << std::endl;    }    return 0;}

总的来说,这个 main 函数创建了一个TCP服务器,并通过调用 io_context.run() 启动服务器并进入事件循环,等待客户端连接请求。如果发生异常,它会捕获异常并打印错误信息。这是一个简单的服务器入口点,用于启动服务器应用程序。

3、客户端

客户端的设计用之前的同步模式即可,客户端不需要异步的方式,因为客户端并不是以并发为主,当然写成异步收发更好一些。 这里代码就不展示了,有兴趣去前一篇文章查看。

运行服务器之后再运行客户端,输入字符串后,就可以收到服务器应答的字符串了。
在这里插入图片描述
在这里插入图片描述
echo应答模式:
在这里插入图片描述

4、隐患

demo示例为仿照asio官网编写的,其中存在隐患,就是当服务器即将发送数据前(调用async_write前),此刻客户端中断,服务器此时调用async_write会触发发送回调函数,判断ec为非0进而执行delete this逻辑回收session。但要注意的是客户端关闭后,在tcp层面会触发读就绪事件,服务器会触发读事件回调函数。在读事件回调函数中判断错误码ec为非0,进而再次执行delete操作,从而造成二次析构,这是极度危险的。
在这里插入图片描述

5、总结

本文介绍了异步的应答服务器设计,但是这种服务器并不会在实际生产中使用,主要有两个原因:

来源地址:https://blog.csdn.net/qq_44918090/article/details/133349652

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-服务器
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯