查找网站备案信息,做网站外包公司,政协网站建设情况汇报,捷讯官网 网站建设文章目录 1、简介2、echo模式应答异步服务器2.1、Session会话类2.2、Server类为服务器接收连接的管理类 3、客户端4、隐患5、总结 1、简介
前文已经介绍了异步操作的api#xff0c;今天写一个简单的异步echo服务器#xff0c;以应答为主。
2、echo模式应答异步服务器
2.1、… 文章目录 1、简介2、echo模式应答异步服务器2.1、Session会话类2.2、Server类为服务器接收连接的管理类 3、客户端4、隐患5、总结 1、简介
前文已经介绍了异步操作的api今天写一个简单的异步echo服务器以应答为主。
2、echo模式应答异步服务器
2.1、Session会话类
Session类主要是处理客户端消息接收发送的会话类为了简单起见我们不考虑粘包问题也不考虑支持手动调用发送的接口只以应答的方式发送和接收固定长度 (1024字节长度) 的数据。
“session.h” :
#pragma once
#includeiostream
#includestring
#includeboost/asio.hppclass 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 是一个预处理指令确保被包含的头文件只会被包含一次防止多次包含和潜在的问题。
#pragma once
#includeiostream
#includestring
#includeboost/asio.hpp类声明 代码定义了一个名为 Session 的 C 类。它有一个构造函数接受一个 boost::asio::io_context 对象的引用并且有两个公共成员函数GetSocket 和 Start。
class Session
{
public:Session(boost::asio::io_context ioc);
public:boost::asio::ip::tcp::socket GetSocket();void Start();
protected:// ...
private:// ...
}; 公共成员函数 Session(boost::asio::io_context ioc)这是 Session 类的构造函数。它接受一个 io_context 的引用作为参数通常用于管理异步I/O操作。 boost::asio::ip::tcp::socket GetSocket()此函数返回一个TCP套接字对象的引用。它可能允许外部代码访问与此会话相关联的套接字。 void Start() 此函数预计启动会话。代码片段没有提供其实际实现但它可能启动一些用于网络通信的异步操作。 受保护的成员函数 这些是受保护的成员函数。它们可能被派生类覆盖或在内部用于处理读取和发送操作。 HandlerRead(const boost::system::error_code err, size_t bytes_transferred) 这个函数似乎是用于处理通过TCP连接接收的数据的回调。它接受错误代码和传输的字节数作为参数。 HandleSend(const boost::system::error_code err) 这个函数似乎是用于处理发送数据完成的回调。它也接受一个错误代码作为参数。
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.hSession::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 类的构造函数的实现。它接受一个 boost::asio::io_context 的引用作为参数并使用该上下文初始化了 socket_。此外它使用 memset 函数将 data_ 数组的内容初始化为零。
Session::Session(boost::asio::io_context io_context): socket_(io_context)
{memset(data_, 0, sizeof(data_));
}
GetSocket 函数: 这个函数返回 socket_ 的引用允许外部代码访问与会话相关联的TCP套接字。
boost::asio::ip::tcp::socket Session::GetSocket() {return socket_;
}
Start 函数在Start方法中我们调用异步读操作监听对端发送的消息。当对端发送数据后触发HandlerRead回调函数: Start 函数似乎是启动会话的函数。它首先使用 memset 将 data_ 数组的内容初始化为零然后调用 socket_ 的 async_read_some 函数启动异步读取操作。当数据到达时会调用 HandlerRead 回调函数处理读取的数据。
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));
}
HandlerRead 函数HandlerRead函数内将收到的数据发送给对端当发送完成后触发HandleSend回调函数: HandlerRead 函数是用于处理异步读取操作完成时的回调。它检查是否有错误发生如果有错误它会输出错误消息并删除会话对象。如果没有错误它输出接收到的数据并通过再次调用 async_read_some 来继续监听接收/读取数据的操作。同时它也启动了异步发送操作将数据发送回客户端。
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));}
}
HandleSend 函数: HandleSend 函数用于处理异步发送操作完成时的回调。它检查是否有错误发生如果有错误它会输出错误消息并删除会话对象。如果没有错误它继续通过再次调用 async_read_some 来监听接收/读取数据的操作。HandleSend函数内又一次监听了读事件如果对端有数据发送过来则触发HandlerRead我们再将收到的数据发回去。从而达到应答式服务的效果。
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
#includeiostream
#includeboost/asio.hpp
#includeSession.hclass 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 的C类该类似乎用于创建和管理一个基于Boost.Asio库的TCP服务器。以下是对这段代码的解释
头文件包含 这些行包括了必要的头文件。#pragma once 用于确保头文件只被包含一次避免多次包含的问题。
#pragma once
#include iostream
#include boost/asio.hpp
#include Session.h类声明 这段代码定义了一个名为 Server 的**C**类该类有一个公共构造函数和两个私有成员函数以及两个私有成员变量。
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 类的构造函数的实现。构造函数接受一个 boost::asio::io_context 对象的引用和一个端口号作为参数。在构造函数内部它初始化了 io_context_ 和 acceptor_ 成员变量。io_context_ 用于管理异步操作而 acceptor_ 用于监听传入的连接。构造函数还调用了 StartAccept 函数来开始接受连接。
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();
}
私有成员函数: StartAccept将要接收连接的acceptor绑定到服务上其内部就是将accpeptor对应的socket描述符绑定到epoll或iocp模型上实现事件驱动。HandleAccept为新连接到来后触发的回调函数。 void StartAccept():StartAccept 函数创建一个新的 Session 对象然后使用 acceptor_ 的 async_accept 函数来异步等待并接受传入的连接。当连接被接受时会调用 HandleAccept 函数来处理连接。
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 HandleAccept(Session session, const boost::system::error_code err):* HandleAccept 函数用于处理接受连接操作的回调。如果没有错误发生它会调用 Session 对象的 Start 方法启动会话。如果发生错误它会删除 Session 对象。然后无论如何它都会继续调用 StartAccept 函数以便等待下一个连接。
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:
#includeserver.hServer::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 类的构造函数的实现。构造函数接受一个 boost::asio::io_context 对象的引用和一个端口号作为参数。在构造函数内部它初始化了 io_context_ 和 acceptor_ 成员变量。io_context_ 用于管理异步操作而 acceptor_ 用于监听传入的连接。构造函数还调用了 StartAccept 函数来开始接受连接。
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();
}
StartAccept 函数: StartAccept 函数创建一个新的 Session 对象然后使用 acceptor_ 的 async_accept 函数来异步等待并接受传入的连接。当连接被接受时会调用 HandleAccept 函数来处理连接。
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));
}
HandleAccept 函数: HandleAccept 函数是用于处理接受连接操作的回调。如果没有错误发生err.value() 0它会输出连接成功的消息并显示客户端的IP地址。然后它调用了 Session 对象的 Start 方法来启动会话。如果有错误发生它会输出错误消息并删除 Session 对象。无论如何最后它会继续调用 StartAccept 函数以便等待下一个连接。
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”:
#includeserver.hint 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服务器并运行它。以下是对代码的详细解释
头文件包含: 这行代码包含了名为 “server.h” 的头文件该头文件应该包含了 Server 类的声明以及其他必要的头文件。
#include server.h
main 函数: boost::asio::io_context io_context;:创建了一个 io_context 对象它用于管理异步操作。Boost.Asio库通常需要一个 io_context 对象来协调和管理异步操作。 Server server(io_context, 9273); 创建了一个 Server 类的对象 server并传递了 io_context 对象和一个端口号在此示例中是9273作为参数。这样做将启动服务器并开始监听指定的端口。 io_context.run(); 调用 io_context 对象的 run 方法开始运行事件循环该事件循环会一直运行直到没有待处理的异步操作。在这里它将一直运行以监听和处理客户端连接请求。 catch (std::exception e) { … }: 这是一个异常处理块用于捕获任何可能抛出的异常。如果发生异常它将打印异常的描述信息。
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、总结
本文介绍了异步的应答服务器设计但是这种服务器并不会在实际生产中使用主要有两个原因:
因为该服务器的发送和接收以应答的方式交互而并不能做到应用层想随意发送的目的也就是未做到完全的收发分离(全双工逻辑)。该服务器未处理粘包序列化以及逻辑和收发线程解耦等问题。该服务器存在二次析构的风险。