返回首页

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();
}