我正在研究BOOST ASIO。 https://www.boost.org/doc/libs/1_83_0/doc/html/boost_asio/examples/cpp14_examples.html
这里有一些例子。
查看blocking_tcp_echo_server, 该示例使用线程作为处理多个客户端的方法。 我的问题是与 async_tcp_echo_server 示例是否有任何功能差异。
blocking_tcp_echo_server
#include <cstdlib>
#include <iostream>
#include <thread>
#include <utility>
#include <boost/asio.hpp>
using boost::asio::ip::tcp;
const int max_length = 1024;
void session(tcp::socket sock)
{
try
{
for (;;)
{
char data[max_length];
boost::system::error_code error;
size_t length = sock.read_some(boost::asio::buffer(data), error);
if (error == boost::asio::error::eof)
break; // Connection closed cleanly by peer.
else if (error)
throw boost::system::system_error(error); // Some other error.
boost::asio::write(sock, boost::asio::buffer(data, length));
}
}
catch (std::exception& e)
{
std::cerr << "Exception in thread: " << e.what() << "\n";
}
}
void server(boost::asio::io_context& io_context, unsigned short port)
{
tcp::acceptor a(io_context, tcp::endpoint(tcp::v4(), port));
for (;;)
{
std::thread(session, a.accept()).detach();
}
}
int main(int argc, char* argv[])
{
try
{
if (argc != 2)
{
std::cerr << "Usage: blocking_tcp_echo_server <port>\n";
return 1;
}
boost::asio::io_context io_context;
server(io_context, std::atoi(argv[1]));
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}
async_tcp_echo_server
#include <cstdlib>
#include <iostream>
#include <memory>
#include <utility>
#include <boost/asio/ts/buffer.hpp>
#include <boost/asio/ts/internet.hpp>
using boost::asio::ip::tcp;
class session
: public std::enable_shared_from_this<session>
{
public:
session(tcp::socket socket)
: socket_(std::move(socket))
{
}
void start()
{
do_read();
}
private:
void do_read()
{
auto self(shared_from_this());
socket_.async_read_some(boost::asio::buffer(data_, max_length),
[this, self](boost::system::error_code ec, std::size_t length)
{
if (!ec)
{
do_write(length);
}
});
}
void do_write(std::size_t length)
{
auto self(shared_from_this());
boost::asio::async_write(socket_, boost::asio::buffer(data_, length),
[this, self](boost::system::error_code ec, std::size_t /*length*/)
{
if (!ec)
{
do_read();
}
});
}
tcp::socket socket_;
enum { max_length = 1024 };
char data_[max_length];
};
class server
{
public:
server(boost::asio::io_context& io_context, short port)
: acceptor_(io_context, tcp::endpoint(tcp::v4(), port)),
socket_(io_context)
{
do_accept();
}
private:
void do_accept()
{
acceptor_.async_accept(socket_,
[this](boost::system::error_code ec)
{
if (!ec)
{
std::make_shared<session>(std::move(socket_))->start();
}
do_accept();
});
}
tcp::acceptor acceptor_;
tcp::socket socket_;
};
int main(int argc, char* argv[])
{
try
{
if (argc != 2)
{
std::cerr << "Usage: async_tcp_echo_server <port>\n";
return 1;
}
boost::asio::io_context io_context;
server s(io_context, std::atoi(argv[1]));
io_context.run();
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}
除了错误报告方面非常轻微(且非常明显)的差异之外,两者之间没有功能差异。
这里的差异最小化,在一个可以运行阻塞和同步的程序中重用尽可能多的代码:
#include <boost/asio.hpp>
#include <cstdlib>
#include <iostream>
#include <thread>
#include <utility>
namespace asio = boost::asio;
using asio::ip::tcp;
using boost::system::error_code;
static constexpr auto max_length = 1024;
namespace blocking {
void session(tcp::socket sock) try {
char data[max_length];
for (error_code ec; size_t length = sock.read_some(asio::buffer(data), ec);) {
if (ec == asio::error::eof)
break; // Connection closed cleanly by peer.
if (ec)
throw boost::system::system_error(ec); // Some other error.
write(sock, asio::buffer(data, length));
}
} catch (std::exception const& e) {
std::cerr << "Exception in thread: " << e.what() << "\n";
}
void server(asio::io_context& io_context, uint16_t port) {
for (tcp::acceptor a(io_context, tcp::endpoint(tcp::v4(), port));;)
std::thread(session, a.accept()).detach();
}
} // namespace blocking
namespace async {
struct session : std::enable_shared_from_this<session> {
session(tcp::socket socket) : socket_(std::move(socket)) {}
void start() { do_read(); }
private:
void do_read() {
socket_.async_read_some( //
asio::buffer(data_), [this, self = shared_from_this()](error_code ec, size_t length) {
if (!ec)
do_write(length);
});
}
void do_write(size_t length) {
async_write( //
socket_, asio::buffer(data_, length),
[this, self = shared_from_this()](error_code ec, size_t /*length*/) {
if (!ec)
do_read();
});
}
tcp::socket socket_;
char data_[max_length];
};
struct server {
server(asio::io_context& io_context, uint16_t port) : acceptor_(io_context, {{}, port}) {
do_accept();
}
private:
void do_accept() {
acceptor_.async_accept([this](error_code ec, tcp::socket s) {
if (!ec)
std::make_shared<session>(std::move(s))->start();
do_accept();
});
}
tcp::acceptor acceptor_;
};
} // namespace async
#include <ranges>
int main(int argc, char** argv) try {
std::vector<std::string_view> args(argv + 1, argv + argc);
bool async = false;
if (auto it = std::ranges::find(args, "async"); it != end(args)) {
args.erase(it);
async = true;
}
uint16_t port = static_cast<uint16_t>(std::atoi(args.at(0).data()));
asio::io_context ioc;
if (async) {
async::server srv(ioc, port);
ioc.run();
} else {
blocking::server(ioc, port);
}
} catch (std::exception const& e) {
std::cerr << "Exception: " << e.what() << "\n";
}
当然,技术差异包括异步服务器不会创建额外的线程。