1.引用 和复制(不用&做参数) 的区别
(1)第一个:肯定是一个原参数操作,另外一个是对原参数 复制一个副本 去操作(开销问题)
(2)第二个:如果是公共参数,比如io_context,他始终只有一个(与内核相关),只能由&
简介
本文基于c++,重点是来掌握c++的异步概念.也是基于boost的asio库
服务端
对话设计
因为是io复用的服务器设计,我们的服务器只有一个端口和ip地址,只就要产生多个 多个会话区
即是 客户端
以防内存泄漏,本设计采用智能指针来控制对象的释放.
using namespace boost::asio::ip;
class session:public std::enable_shared_from_this<session> {
public:
session(boost::asio::io_context &io_context):socket_(io_context){
};
void start() {
auto self(shared_from_this());
boost::asio::async_read_until(socket_,buffer,'\n',[self](boost::system::error_code ec,std::size_t len) {
if(!ec) {
std::istream is(&self->buffer);
std::string line;
std::getline(is,line);
std::cout<<"Received: "<<line<<std::endl;
boost::asio::async_write(self->socket_,boost::asio::buffer("Receive is finished"),[self](boost::system::error_code ec) {
if(!ec) {
self->start();
}
});
}
else
std::cout<<ec.failed()<<std::endl;
});
}
private:
tcp::socket socket_;
boost::asio::streambuf buffer;
};
这个采用递归一直读写
boost::asio::async_read_until(socket, buffer, delimiter, read_handler);
- socket:
类型:boost::asio::basic_stream_socket 或其他支持异步读取的套接字类型。
作用:指定从哪个套接字读取数据。- buffer:
类型:boost::asio::streambuf 或其他动态缓冲区类型。
作用:存储读取到的数据。async_read_until 会将读取到的数据存储到这个缓冲区中。- delimiter:
类型:char 或 std::string。
作用:指定读取操作的结束条件。当读取到的数据中包含这个分隔符时,读取操作将停止。例如,如果你指定分隔符为 \n,则读取操作会在遇到换行符时停止。- read_handler:
类型:回调函数或 lambda 表达式。
作用:在读取操作完成时被调用。回调函数的签名通常为:void read_handler(const boost::system::error_code& ec, std::size_t bytes_transferred);
boost::asio::async_write(socket, buffers, write_handler);
- socket:
类型:boost::asio::basic_stream_socket 或其他支持异步写入的流类型。
作用:指定将数据写入到哪个套接字或流中。- buffers:
类型:boost::asio::const_buffer 或 std::vectorboost::asio::const_buffer。
作用:指定要写入的数据。const_buffer 是一个表示常量缓冲区的类,可以包含指向数据的指针和数据的大小。你可以使用 - - - —boost::asio::buffer 函数来创建 const_buffer 对象。- write_handler:
类型:回调函数或 lambda 表达式。
作用:在写入操作完成时被调用。回调函数的签名通常为:
服务端
class server {
public:
server(boost::asio::io_context &io_context,short port):acceptor_(io_context,tcp::endpoint(tcp::v4(),port)),io_context_(io_context) {
std::cout<<"Server is running on port: "<<port<<std::endl;
start_accept();
}
void start_accept() {
auto new_session=std::make_shared<session>(io_context_);
acceptor_.async_accept(new_session->socket(),[this,new_session](boost::system::error_code ec) {
if(!ec) {
new_session->start();
}
start_accept();//采用递归来不断连接
});
}
private:
boost::asio::io_context &io_context_;
tcp::acceptor acceptor_;
};
acceptor_.async_accept
参数为 socket:
类型:boost::asio::basic_stream_socket 或其他支持异步写入的流类型。(其实是客户端的socekt)
回调函数
io_context
为服务器的socket
运行
int main() {
try {
boost::asio::io_context io_context;
server s(io_context,8080);
std::vector<std::thread>threads(5);
for(int i=0;i<threads.size();i++) {
threads[i]=std::thread([&io_context]() {
io_context.run();
});
}
for(auto &thread: threads) {
thread.join();
}
} catch (std::exception ec) {
std::cout<<ec.what()<<std::endl;
}
}
使用了多个进程来运行io_context.run();
客户端
tcp::resolver
在 Boost.Asio 中,boost::asio::ip::tcp::resolver 是一个用于解析主机名和端口名称的类。它将主机名和端口名称转换为一个或多个 tcp::endpoint 对象,这些对象表示可以连接到的网络地址和端口。
tcp::resolver 的作用
tcp::resolver 的主要作用是将主机名和端口名称解析为一个或多个 tcp::endpoint 对象。这个过程通常涉及以下步骤:
- DNS 查询:将主机名解析为一个或多个 IP 地址。
- 端口解析:将端口名称解析为一个端口号。
- 生成 tcp::endpoint:将解析得到的 IP 地址和端口号组合成一个或多个 tcp::endpoint 对象。
方法
tcp::resolver::query()
query(const std::string& host, const std::string& service);
void async_resolve(const query& q,ResolveHandler handler);
q:一个 tcp::resolver::query 对象,包含主机名和端口名称。
handler:解析完成后的回调函数。回调函数的签名通常为:void handler(const boost::system::error_code& ec, tcp::resolver::results_type endpoints);
返回一个或多个endpoint对象
boost::asio::async_connect(socket_, endpoints, [this, self](boost::system::error_code ec, tcp::endpoint)
- 使用 async_connect 异步连接到解析得到的 tcp::endpoint。
#include <iostream>
#include <boost/asio.hpp>
#include <boost/enable_shared_from_this.hpp>
using boost::asio::ip::tcp;
class client : public std::enable_shared_from_this<client>
{
public:
client(boost::asio::io_context& io_context)
: socket_(io_context), resolver_(io_context)
{
}
void start(const std::string& server, const std::string& port)
{
auto self(shared_from_this());
tcp::resolver::query query(server, port);
resolver_.async_resolve(query,
[this, self](boost::system::error_code ec, tcp::resolver::results_type endpoints)
{
if (!ec)
{
boost::asio::async_connect(socket_, endpoints,
[this, self](boost::system::error_code ec, tcp::endpoint)
{
if (!ec)
{
do_write();
}
else
{
std::cerr << "Connection failed: " << ec.message() << std::endl;
}
});
}
else
{
std::cerr << "Resolve failed: " << ec.message() << std::endl;
}
});
}
private:
void do_write()
{
auto self(shared_from_this());
std::string message = "Hello, server!\n";
boost::asio::async_write(socket_, boost::asio::buffer(message),
[this, self](boost::system::error_code ec, std::size_t /*length*/)
{
if (!ec)
{
// 继续读取数据
do_read();
}
else
{
std::cerr << "Write failed: " << ec.message() << std::endl;
}
});
}
void do_read()
{
auto self(shared_from_this());
boost::asio::async_read_until(socket_, buffer_, '\n',
[this, self](boost::system::error_code ec, std::size_t length)
{
if (!ec)
{
std::istream is(&buffer_);
std::string line;
std::getline(is, line);
std::cout << "Received from server: " << line << std::endl;
// 清空缓冲区
buffer_.consume(length);
// 继续读取数据
do_read();
}
else
{
std::cerr << "Read failed: " << ec.message() << std::endl;
}
});
}
tcp::socket socket_;
tcp::resolver resolver_;
boost::asio::streambuf buffer_;
};
int main()
{
try
{
boost::asio::io_context io_context;
auto c = std::make_shared<client>(io_context);
c->start("127.0.0.1", "8080");
io_context.run();
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << std::endl;
}
return 0;
}