boost asio tcp
使用 boost asio 搭建 tcp 服务器和客户端
异步服务端
#include <boost/asio.hpp>
#include <boost/program_options.hpp>
#include <cstddef>
#include <cstdint>
#include <exception>
#include <format>
#include <iostream>
#include <string>
#include <utility>
#include <vector>
using namespace std;
namespace asio = boost::asio;
namespace ip = asio::ip;
using tcp = ip::tcp;
using uint16 = uint16_t;
namespace po = boost::program_options;
asio::awaitable<void> session(tcp::socket socket) {
try {
char data[1024];
for (;;) {
size_t n = co_await socket.async_read_some(asio::buffer(data),
asio::use_awaitable);
co_await asio::async_write(socket, asio::buffer(data, n),
asio::use_awaitable);
}
} catch (const exception &err) {
cerr << err.what() << endl;
}
}
asio::awaitable<void> server(string addr, int port) {
try {
auto executor = co_await asio::this_coro::executor;
auto address = ip::make_address(addr);
auto ep = tcp::endpoint(address, port);
tcp::acceptor acceptor(executor, ep);
cout << format("listen on {}\n", print_endpoint(ep));
for (;;) {
auto socket = co_await acceptor.async_accept(asio::use_awaitable);
asio::co_spawn(executor, session(std::move(socket)), asio::detached);
}
} catch (const exception &err) {
cerr << err.what() << endl;
}
}
int main(int argc, char *argv[]) {
try {
string mode = "";
if (argc != 0) {
mode = argv[1];
}
if (argc == 1 || (mode != "server" && mode != "client")) {
cout << R"(SongBox:
Usage:
songbox server [options]
songbox client [options]
)";
return 0;
}
if (mode == "server") {
po::variables_map vm;
po::options_description serverOd("SongBox Server");
string addr = "";
uint16 port = 0;
bool help = false;
serverOd.add_options()("help,h", po::bool_switch(&help), "print help")(
"listen,l", po::value<string>(&addr),
"listen addr")("port,p", po::value<uint16>(&port), "port");
po::store(po::parse_command_line(argc - 1, argv + 1, serverOd), vm);
po::notify(vm);
if (vm.empty() || help) {
cout << serverOd;
return 0;
}
asio::io_context io;
asio::co_spawn(io, server(addr, port), asio::detached);
io.run();
return 0;
}
} catch (const exception &err) {
cerr << err.what() << endl;
}
}
同步客户端
if (mode == "client") {
po::variables_map vm;
po::options_description clientOd("SongBox Client");
string addr = "";
uint16 port = 0;
bool help = false;
clientOd.add_options()("help,h", po::bool_switch(&help), "print help")(
"server,s", po::value<string>(&addr),
"server addr")("port,p", po::value<uint16>(&port), "port");
po::store(po::parse_command_line(argc - 1, argv + 1, clientOd), vm);
po::notify(vm);
if (vm.empty() || help) {
cout << clientOd;
return 0;
}
asio::io_context io;
tcp ::socket socket(io);
auto address = ip::make_address(addr);
auto ep = tcp::endpoint(address, port);
socket.connect(ep);
asio::write(socket, asio::buffer("hello"));
char data[1024];
for (;;) {
size_t n = socket.read_some(asio::buffer(data));
cout << string{data, n};
}
return 0;
}
服务端类和信号优雅停止acceptor
class Server {
public:
explicit Server(asio::io_context &io, string &addr, int port)
: addr_(addr), port_(port), io_(io) {}
task<void> start() {
auto address = ip::make_address(addr_);
auto ep = tcp::endpoint(address, port_);
acceptor_.emplace(io_);
acceptor_->open(ep.protocol());
acceptor_->set_option(tcp::acceptor::reuse_address(true));
acceptor_->bind(ep);
acceptor_->listen();
for (;;) {
auto socket = co_await acceptor_->async_accept(asio::use_awaitable);
asio::co_spawn(io_, handle_(std::move(socket)), asio::detached);
}
cout << format("listen on {}\n", print_endpoint(ep));
co_return;
}
void stop() {
if (acceptor_.has_value()) {
cout << "stop server...\n";
acceptor_->close();
}
}
private:
string addr_;
int port_;
asio::io_context &io_;
optional<tcp::acceptor> acceptor_;
task<void> handle_(tcp::socket socket) {
try {
char data[1024];
for (;;) {
size_t n = co_await socket.async_read_some(asio::buffer(data),
asio::use_awaitable);
co_await asio::async_write(socket, asio::buffer(data, n),
asio::use_awaitable);
}
} catch (const exception &err) {
cerr << err.what() << endl;
}
}
};
int main(){
asio::io_context io;
asio::signal_set signal(io, SIGINT, SIGTERM);
Server server(io, addr, port);
signal.async_wait([&](const boost::system::error_code &, int) {
cout << "stop...\n";
server.stop();
io.stop();
});
asio::co_spawn(io, server.start(), asio::detached);
io.run();
}