From f8afae6eb30fbe79bcd0fbf8b88f4de5877c24a4 Mon Sep 17 00:00:00 2001 From: Kingsley Chen Date: Wed, 24 Jun 2020 18:40:12 +0800 Subject: [PATCH] Add asio standalone example (#133) * Add asio standalone example * Mark VERSION explicitly Without this version information, CPM's versioning mechanism may break in some cases. Also add comments for compile definition on Windows. * Use aync-tcp-echo-server as the example * Add boost software license to the demo source Co-authored-by: Lars Melchior Co-authored-by: Lars Melchior --- examples/asio-standalone/CMakeLists.txt | 79 ++++++++++++++++ examples/asio-standalone/main.cpp | 118 ++++++++++++++++++++++++ 2 files changed, 197 insertions(+) create mode 100644 examples/asio-standalone/CMakeLists.txt create mode 100644 examples/asio-standalone/main.cpp diff --git a/examples/asio-standalone/CMakeLists.txt b/examples/asio-standalone/CMakeLists.txt new file mode 100644 index 0000000..71b2030 --- /dev/null +++ b/examples/asio-standalone/CMakeLists.txt @@ -0,0 +1,79 @@ +cmake_minimum_required(VERSION 3.14 FATAL_ERROR) + +project(CPMExampleASIOStandalone) + +# ---- Dependencies ---- + +include(../../cmake/CPM.cmake) + +find_package(Threads REQUIRED) + +CPMAddPackage( + NAME asio + VERSION 1.16.1 + GITHUB_REPOSITORY chriskohlhoff/asio + GIT_TAG asio-1-16-1 # asio uses non-standard version tag, we must specify GIT_TAG +) + +# ASIO doesn't use CMake, we have to configure it manually. +# Extra notes for using on Windows: +# 1) If _WIN32_WINNT is not set, ASIO assumes _WIN32_WINNT=0x0501, i.e. Windows XP target, +# which is definitely not the platform which most users target. +# 2) WIN32_LEAN_AND_MEAN is defined to make Winsock2 work. +if(asio_ADDED) + add_library(asio INTERFACE) + + target_include_directories(asio + INTERFACE ${asio_SOURCE_DIR}/asio/include + ) + + target_compile_definitions(asio + INTERFACE + ASIO_STANDALONE + ASIO_NO_DEPRECATED + ) + + target_link_libraries(asio + INTERFACE + Threads::Threads + ) + + if(WIN32) + # macro see @ https://stackoverflow.com/a/40217291/1746503 + macro(get_win32_winnt version) + if (CMAKE_SYSTEM_VERSION) + set(ver ${CMAKE_SYSTEM_VERSION}) + string(REGEX MATCH "^([0-9]+).([0-9])" ver ${ver}) + string(REGEX MATCH "^([0-9]+)" verMajor ${ver}) + # Check for Windows 10, b/c we'll need to convert to hex 'A'. + if ("${verMajor}" MATCHES "10") + set(verMajor "A") + string(REGEX REPLACE "^([0-9]+)" ${verMajor} ver ${ver}) + endif ("${verMajor}" MATCHES "10") + # Remove all remaining '.' characters. + string(REPLACE "." "" ver ${ver}) + # Prepend each digit with a zero. + string(REGEX REPLACE "([0-9A-Z])" "0\\1" ver ${ver}) + set(${version} "0x${ver}") + endif() + endmacro() + + if(NOT DEFINED _WIN32_WINNT) + get_win32_winnt(ver) + set(_WIN32_WINNT ${ver}) + endif() + + message(STATUS "Set _WIN32_WINNET=${_WIN32_WINNT}") + + target_compile_definitions(asio + INTERFACE + _WIN32_WINNT=${_WIN32_WINNT} + WIN32_LEAN_AND_MEAN + ) + endif() +endif() + +# ---- Executable ---- + +add_executable(CPMExampleASIOStandalone main.cpp) +target_link_libraries(CPMExampleASIOStandalone asio) diff --git a/examples/asio-standalone/main.cpp b/examples/asio-standalone/main.cpp new file mode 100644 index 0000000..c7d9564 --- /dev/null +++ b/examples/asio-standalone/main.cpp @@ -0,0 +1,118 @@ +// +// async_tcp_echo_server.cpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. +// (See http://www.boost.org/LICENSE_1_0.txt) +// + +#include +#include +#include +#include + +#include "asio.hpp" + +// An asynchronous tcp echo server. +// See https://think-async.com/Asio/asio-1.16.1/src/examples/cpp11/echo/async_tcp_echo_server.cpp + +using asio::ip::tcp; + +class session + : public std::enable_shared_from_this +{ +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(asio::buffer(data_, max_length), + [this, self](std::error_code ec, std::size_t length) + { + if (!ec) + { + do_write(length); + } + }); + } + + void do_write(std::size_t length) + { + auto self(shared_from_this()); + asio::async_write(socket_, asio::buffer(data_, length), + [this, self](std::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(asio::io_context& io_context, short port) + : acceptor_(io_context, tcp::endpoint(tcp::v4(), port)) + { + do_accept(); + } + +private: + void do_accept() + { + acceptor_.async_accept( + [this](std::error_code ec, tcp::socket socket) + { + if (!ec) + { + std::make_shared(std::move(socket))->start(); + } + + do_accept(); + }); + } + + tcp::acceptor acceptor_; +}; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 2) + { + std::cerr << "Usage: async_tcp_echo_server \n"; + return 1; + } + + 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; +}