C++23 coroutine network framework
Explore the docs »
View Demo
·
Report Bug
·
Request Feature
Table of Contents
Based on the libuv
event loop, use C++20 stackless coroutines
to implement network components, and provide channel
to send and receive data between tasks.
Required compiler:
- GCC >= 14
- LLVM >= 18
- MSVC >= 19.38
Export environment variables:
- VCPKG_INSTALLATION_ROOT
- ANDROID_NDK_HOME(Android)
-
Linux
cmake -B build -DCMAKE_TOOLCHAIN_FILE="${VCPKG_INSTALLATION_ROOT}/scripts/buildsystems/vcpkg.cmake" && cmake --build build -j$(nproc)
-
Android
# set "ANDROID_PLATFORM" for dependencies installed by vcpkg: echo 'set(VCPKG_CMAKE_SYSTEM_VERSION 24)' >> "${VCPKG_INSTALLATION_ROOT}/triplets/community/arm64-android.cmake" cmake -B build -DCMAKE_TOOLCHAIN_FILE="${VCPKG_INSTALLATION_ROOT}/scripts/buildsystems/vcpkg.cmake" -DVCPKG_CHAINLOAD_TOOLCHAIN_FILE="${ANDROID_NDK_HOME}/build/cmake/android.toolchain.cmake" -DVCPKG_TARGET_TRIPLET=arm64-android -DANDROID_ABI=arm64-v8a -DANDROID_PLATFORM=android-24 && cmake --build build -j$(nproc)
-
Windows(Developer PowerShell)
cmake -B build -DCMAKE_TOOLCHAIN_FILE="$Env:VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake" && cmake --build build -j $env:NUMBER_OF_PROCESSORS
Install asyncio
from the vcpkg
private registry:
-
Create a
vcpkg-configuration.json
file in the project root directory:{ "registries": [ { "kind": "git", "repository": "https://github.com/Hackerl/vcpkg-registry", "baseline": "56e949885c4f22d5711523f360ac92d73f7a8495", "packages": [ "asyncio", "zero" ] } ] }
The
baseline
defines the minimum version ofasyncio
that will be installed. The one used above might be outdated, so please update it as necessary. -
Create a
vcpkg.json
file in the project root directory:{ "name": "project name", "version-string": "1.0.0", "builtin-baseline": "9224b3bbd8df24999d85720b1d005dd6f969ade0", "dependencies": [ "asyncio" ] }
-
Add the following to the
CMakeLists.txt
file:find_package(asyncio CONFIG REQUIRED) target_link_libraries(custom_target PRIVATE asyncio::asyncio-main)
Using the HTTP Client
in a C++
project has never been easier:
#include <asyncio/http/request.h>
asyncio::task::Task<void, std::error_code> asyncMain(const int argc, char *argv[]) {
const auto url = asyncio::http::URL::from("https://www.google.com");
CO_EXPECT(url);
auto requests = asyncio::http::Requests::make();
CO_EXPECT(requests);
auto response = co_await requests->get(*url);
CO_EXPECT(response);
const auto content = co_await response->string();
CO_EXPECT(content);
fmt::print("{}", *content);
co_return {};
}
We use
asyncMain
instead ofmain
as the entry point, andCO_EXPECT
is used to check for errors and throw them upwards.
#include <asyncio/net/stream.h>
#include <asyncio/signal.h>
#include <zero/cmdline.h>
asyncio::task::Task<void, std::error_code> handle(asyncio::net::TCPStream stream) {
const auto address = stream.remoteAddress();
CO_EXPECT(address);
fmt::print("connection[{}]\n", *address);
while (true) {
std::string message;
message.resize(1024);
const auto n = co_await stream.read(std::as_writable_bytes(std::span{message}));
CO_EXPECT(n);
if (*n == 0)
break;
message.resize(*n);
fmt::print("receive message: {}\n", message);
CO_EXPECT(co_await stream.writeAll(std::as_bytes(std::span{message})));
}
co_return {};
}
asyncio::task::Task<void, std::error_code> serve(asyncio::net::TCPListener listener) {
std::expected<void, std::error_code> result;
asyncio::task::TaskGroup group;
while (true) {
auto stream = co_await listener.accept();
if (!stream) {
result = std::unexpected{stream.error()};
break;
}
auto task = handle(*std::move(stream));
group.add(task);
task.future().fail([](const auto &ec) {
fmt::print(stderr, "unhandled error: {} ({})\n", ec.message(), ec);
});
}
co_await group;
co_return result;
}
asyncio::task::Task<void, std::error_code> asyncMain(const int argc, char *argv[]) {
zero::Cmdline cmdline;
cmdline.add<std::string>("host", "remote host");
cmdline.add<std::uint16_t>("port", "remote port");
cmdline.parse(argc, argv);
const auto host = cmdline.get<std::string>("host");
const auto port = cmdline.get<std::uint16_t>("port");
auto listener = asyncio::net::TCPListener::listen(host, port);
CO_EXPECT(listener);
auto signal = asyncio::Signal::make();
CO_EXPECT(signal);
co_return co_await race(
serve(*std::move(listener)),
signal->on(SIGINT).transform([](const int) {
})
);
}
Start the server with
./server 127.0.0.1 8000
, and gracefully exit by pressingctrl + c
in the terminal.
#include <asyncio/net/stream.h>
#include <asyncio/time.h>
#include <zero/cmdline.h>
asyncio::task::Task<void, std::error_code> asyncMain(const int argc, char *argv[]) {
using namespace std::chrono_literals;
using namespace std::string_view_literals;
zero::Cmdline cmdline;
cmdline.add<std::string>("host", "remote host");
cmdline.add<std::uint16_t>("port", "remote port");
cmdline.parse(argc, argv);
const auto host = cmdline.get<std::string>("host");
const auto port = cmdline.get<std::uint16_t>("port");
auto stream = co_await asyncio::net::TCPStream::connect(host, port);
CO_EXPECT(stream);
while (true) {
CO_EXPECT(co_await stream->writeAll(std::as_bytes(std::span{"hello world"sv})));
std::string message;
message.resize(1024);
const auto n = co_await stream->read(std::as_writable_bytes(std::span{message}));
CO_EXPECT(n);
if (*n == 0)
break;
message.resize(*n);
fmt::print("receive message: {}\n", message);
co_await asyncio::sleep(1s);
}
co_return {};
}
Connect to the server with
./client 127.0.0.1 8000
.
For more examples, please refer to the Documentation
- HTTP Server
See the open issues for a full list of proposed features (and known issues).
Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are greatly appreciated.
If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement". Don't forget to give the project a star! Thanks again!
- Fork the Project
- Create your Feature Branch (
git checkout -b feature/AmazingFeature
) - Commit your Changes (
git commit -m 'Add some AmazingFeature'
) - Push to the Branch (
git push origin feature/AmazingFeature
) - Open a Pull Request
Distributed under the Apache 2.0 License. See LICENSE
for more information.
Hackerl - @Hackerl - [email protected]
Project Link: https://github.com/Hackerl/asyncio