Loading

boost asio 与 自定义通信协议的解包

先贴代码,利用了boost asio协程,所以代码基本是同步的编写,但实际是异步地执行。

#define BOOST_ASIO_HAS_CO_AWAIT

#include <boost/asio/awaitable.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/asio/co_spawn.hpp>
#include <boost/asio/connect.hpp>
#include <boost/asio/detached.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/read.hpp>
#include <boost/asio/signal_set.hpp>
#include <boost/asio/this_coro.hpp>
#include <boost/asio/use_awaitable.hpp>
#include <boost/asio/write.hpp>
#include <chrono>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <iostream>
#include <memory>
#include <string>

using boost::asio::awaitable;
using boost::asio::co_spawn;
using boost::asio::detached;
using boost::asio::use_awaitable;
using boost::asio::ip::tcp;
namespace this_coro = boost::asio::this_coro;

constexpr int32_t kHeadLen = 4;
constexpr int32_t kMaxBodyLen = 1024 * 1024;
const char *kPort = "55555";
const char *kAddress = "127.0.0.1";

class Message {
public:
  Message() {}
  ~Message() {}

  bool DecodeHeader() {
    body_length_ = atoi(header_);
    if (body_length_ > kMaxBodyLen) {
      body_length_ = 0;
      return false;
    }
    return true;
  }
  void EncodeHeader() { std::sprintf(header_, "%4d", body_length_); }

  void InitBodyBuffer(const int32_t body_len) {
    std::shared_ptr<char> ptr(new char[body_len],
                              std::default_delete<char[]>());
    body_ = ptr;
    body_length_ = body_len;
  }
  char *GetHeaderBuffer() { return header_; }
  char *GetBodyBuffer() { return body_.get(); }
  int32_t HeaderLen() { return kHeadLen; }
  int32_t BodyLen() { return body_length_; }

private:
  int32_t body_length_ = 0;

  char header_[kHeadLen + 1] = {'0'};
  std::shared_ptr<char> body_;
};

awaitable<void> client(tcp::socket s, const int32_t index) {
  Message message;
  std::string body = std::to_string(index);
  message.InitBodyBuffer(body.length());
  std::strncpy(message.GetBodyBuffer(), body.c_str(), body.length());

  message.EncodeHeader();

  co_await async_write(
      s, boost::asio::buffer(message.GetHeaderBuffer(), message.HeaderLen()),
      use_awaitable);

  co_await async_write(
      s, boost::asio::buffer(message.GetBodyBuffer(), message.BodyLen()),
      use_awaitable);

  char data_recv[1024];
  std::size_t n = co_await async_read(
      s, boost::asio::buffer(data_recv, message.BodyLen()), use_awaitable);

  std::cout << "Reply is: ";
  std::cout.write(data_recv, n);
  std::cout << "\n";
}

awaitable<void> server_handler(tcp::socket socket) {
  Message message;
  std::size_t n = co_await async_read(
      socket,
      boost::asio::buffer(message.GetHeaderBuffer(), message.HeaderLen()),
      use_awaitable);

  if (!message.DecodeHeader()) {
    std::cout << "Decode header fail.\n";
    co_return;
  }

  message.InitBodyBuffer(message.BodyLen());
  n = co_await async_read(
      socket, boost::asio::buffer(message.GetBodyBuffer(), message.BodyLen()),
      use_awaitable);

  std::cout << "Recieve is: ";
  std::cout.write(message.GetBodyBuffer(), n);
  std::cout << "\n";

  n = co_await async_write(
      socket, boost::asio::buffer(message.GetBodyBuffer(), message.BodyLen()),
      use_awaitable);
}

awaitable<void> start_all_client() {
  auto executor = co_await this_coro::executor;
  for (int32_t i = 0; i < 10000; ++i) {
    tcp::socket s(executor);
    tcp::resolver resolver(executor);

    co_await boost::asio::async_connect(s, resolver.resolve(kAddress, kPort),
                                        use_awaitable);
    co_spawn(executor, client(std::move(s), i), detached);
  }
}

awaitable<void> listener() {
  auto executor = co_await this_coro::executor;
  tcp::acceptor acceptor(executor, {tcp::v4(), 55555});
  for (;;) {
    tcp::socket socket = co_await acceptor.async_accept(use_awaitable);
    co_spawn(executor, server_handler(std::move(socket)), detached);
  }
}

int main(int argc, char **argv) {
  if (argc != 2) {
    std::cout << "[Role] 1:server 0:client\n";
    std::cout << "[Usage] ./test1 Role\n";
    return -1;
  }
  int32_t role = std::stoi(argv[1]);

  boost::asio::io_context io_context(1);
  boost::asio::signal_set signals(io_context, SIGINT, SIGTERM);
  signals.async_wait([&](auto, auto) { io_context.stop(); });

  if (role == 1) {
    co_spawn(io_context, listener(), detached);
  } else if (role == 0) {
    co_spawn(io_context, start_all_client(), detached);
  }
  io_context.run();

  return 0;
}

CMakeLists.txt如下

cmake_minimum_required(VERSION 3.6)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

set(CMAKE_CXX_COMPILER "clang++")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++ -lc++abi")
set(CMAKE_CXX_FLAGS "-fcoroutines-ts --stdlib=libc++ -Xclang -fconcepts-ts")

find_package(Boost COMPONENTS system)

include_directories(
        ${BOOST_INCLUDE_DIRS}
)

add_executable(echo echo.cpp)

target_link_libraries(echo
        ${Boost_LIBRARIES}
        pthread
)

posted @ 2021-07-21 22:57  KingHowe  阅读(465)  评论(0编辑  收藏  举报