Initial commit
This commit is contained in:
85
.clang-format
Normal file
85
.clang-format
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
AccessModifierOffset: -2
|
||||||
|
AlignAfterOpenBracket: AlwaysBreak
|
||||||
|
AlignConsecutiveMacros: false
|
||||||
|
AlignConsecutiveAssignments: false
|
||||||
|
AlignConsecutiveDeclarations: false
|
||||||
|
AlignEscapedNewlines: DontAlign
|
||||||
|
AlignOperands: false
|
||||||
|
AlignTrailingComments: false
|
||||||
|
AllowAllArgumentsOnNextLine: false
|
||||||
|
AllowAllConstructorInitializersOnNextLine: false
|
||||||
|
AllowAllParametersOfDeclarationOnNextLine: false
|
||||||
|
AllowShortBlocksOnASingleLine: false
|
||||||
|
AllowShortCaseLabelsOnASingleLine: false
|
||||||
|
AllowShortFunctionsOnASingleLine: Empty
|
||||||
|
AllowShortIfStatementsOnASingleLine: Never
|
||||||
|
AllowShortLambdasOnASingleLine: All
|
||||||
|
AllowShortLoopsOnASingleLine: false
|
||||||
|
AlwaysBreakAfterReturnType: None
|
||||||
|
AlwaysBreakBeforeMultilineStrings: true
|
||||||
|
AlwaysBreakTemplateDeclarations: Yes
|
||||||
|
BinPackArguments: false
|
||||||
|
BinPackParameters: false
|
||||||
|
BreakBeforeBinaryOperators: NonAssignment
|
||||||
|
BreakBeforeBraces: Attach
|
||||||
|
BreakBeforeTernaryOperators: true
|
||||||
|
BreakConstructorInitializers: AfterColon
|
||||||
|
BreakInheritanceList: AfterColon
|
||||||
|
BreakStringLiterals: false
|
||||||
|
ColumnLimit: 80
|
||||||
|
CompactNamespaces: false
|
||||||
|
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||||
|
ConstructorInitializerIndentWidth: 4
|
||||||
|
ContinuationIndentWidth: 4
|
||||||
|
Cpp11BracedListStyle: true
|
||||||
|
DerivePointerAlignment: false
|
||||||
|
FixNamespaceComments: true
|
||||||
|
IncludeBlocks: Regroup
|
||||||
|
IncludeCategories:
|
||||||
|
- Regex: '^<ext/.*\.h>'
|
||||||
|
Priority: 2
|
||||||
|
SortPriority: 0
|
||||||
|
CaseSensitive: false
|
||||||
|
- Regex: '^<.*\.h>'
|
||||||
|
Priority: 1
|
||||||
|
SortPriority: 0
|
||||||
|
CaseSensitive: false
|
||||||
|
- Regex: '^<.*'
|
||||||
|
Priority: 2
|
||||||
|
SortPriority: 0
|
||||||
|
CaseSensitive: false
|
||||||
|
- Regex: '.*'
|
||||||
|
Priority: 3
|
||||||
|
SortPriority: 0
|
||||||
|
CaseSensitive: false
|
||||||
|
IncludeIsMainRegex: '([-_](test|unittest))?$'
|
||||||
|
IndentCaseLabels: true
|
||||||
|
IndentPPDirectives: BeforeHash
|
||||||
|
IndentWidth: 4
|
||||||
|
IndentWrappedFunctionNames: false
|
||||||
|
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||||
|
MaxEmptyLinesToKeep: 1
|
||||||
|
NamespaceIndentation: Inner
|
||||||
|
PointerAlignment: Left
|
||||||
|
ReflowComments: false
|
||||||
|
SortIncludes: true
|
||||||
|
SortUsingDeclarations: true
|
||||||
|
SpaceAfterCStyleCast: false
|
||||||
|
SpaceAfterLogicalNot: false
|
||||||
|
SpaceAfterTemplateKeyword: false
|
||||||
|
SpaceBeforeAssignmentOperators: true
|
||||||
|
SpaceBeforeCpp11BracedList: true
|
||||||
|
SpaceBeforeCtorInitializerColon: true
|
||||||
|
SpaceBeforeInheritanceColon: false
|
||||||
|
SpaceBeforeParens: ControlStatements
|
||||||
|
SpaceBeforeRangeBasedForLoopColon: true
|
||||||
|
SpaceInEmptyParentheses: false
|
||||||
|
SpacesBeforeTrailingComments: 2
|
||||||
|
SpacesInAngles: false
|
||||||
|
SpacesInCStyleCastParentheses: false
|
||||||
|
SpacesInContainerLiterals: false
|
||||||
|
SpacesInParentheses: false
|
||||||
|
SpacesInSquareBrackets: false
|
||||||
|
Standard: Cpp11
|
||||||
|
TabWidth: 4
|
||||||
|
UseTab: Never
|
||||||
4
Makefile
Normal file
4
Makefile
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
build-md:
|
||||||
|
nvim --headless -c "execute 'Neorg export to-file TUTORIAL.md' | sleep 100m | q" norg/tutorial.norg
|
||||||
|
markdown-toc -i TUTORIAL.md
|
||||||
|
nvim --headless -c "execute 'Neorg export to-file README.md' | sleep 100m | q" norg/readme.norg
|
||||||
26
README.md
Normal file
26
README.md
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# 📚 Boost.Asio: Асинхронность в C++
|
||||||
|
|
||||||
|
Руководство по созданию клиент-серверных систем с помощью C++, библиотек Boost.Asio и Boost.Beast.
|
||||||
|
|
||||||
|
<h1 align="center">
|
||||||
|
|
||||||
|
[ЧИТАТЬ ОНЛАЙН](./TUTORIAL.md)
|
||||||
|
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
|
||||||
|
Руководство находится в стадии перевода
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# 💡 О руководстве
|
||||||
|
|
||||||
|
Это руководство является вольным переводом [серии туториалов](https://dens.website/tutorials/cpp-asio). Поскольку перевод вольный, в нем могут быть некоторые отступления от изначального текста.
|
||||||
|
|
||||||
|
|
||||||
|
# 🎈 Помощь в переводе
|
||||||
|
|
||||||
|
В переводе могут быть некоторые неточности, которые неправильно преподносят суть тех или иных вещей. Если вы нашли подобную неточность, откройте Issue или создайте Pull Request. Спасибо!
|
||||||
1073
TUTORIAL.md
Normal file
1073
TUTORIAL.md
Normal file
File diff suppressed because it is too large
Load Diff
19
code/simple_server.cpp
Normal file
19
code/simple_server.cpp
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
#include <boost/asio.hpp>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
std::uint16_t port = 15001;
|
||||||
|
|
||||||
|
boost::asio::io_context io_context;
|
||||||
|
boost::asio::ip::udp::endpoint receiver(boost::asio::ip::udp::v4(), port);
|
||||||
|
boost::asio::ip::udp::socket socket(io_context, receiver);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
char buffer[65536];
|
||||||
|
boost::asio::ip::udp::endpoint sender;
|
||||||
|
std::size_t bytes_transferred =
|
||||||
|
socket.receive_from(boost::asio::buffer(buffer), sender);
|
||||||
|
socket.send_to(boost::asio::buffer(buffer, bytes_transferred), sender);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
56
code/tcp_async_server.cpp
Normal file
56
code/tcp_async_server.cpp
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
#include <boost/asio.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
class session: public std::enable_shared_from_this<session> {
|
||||||
|
public:
|
||||||
|
session(boost::asio::ip::tcp::socket&& socket) :
|
||||||
|
socket(std::move(socket)) {}
|
||||||
|
|
||||||
|
void start() {
|
||||||
|
boost::asio::async_read_until(
|
||||||
|
socket,
|
||||||
|
streambuf,
|
||||||
|
'\n',
|
||||||
|
[self = shared_from_this()](
|
||||||
|
boost::system::error_code error,
|
||||||
|
std::size_t bytes_transferred) {
|
||||||
|
std::cout << std::istream(&self->streambuf).rdbuf();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
boost::asio::ip::tcp::socket socket;
|
||||||
|
boost::asio::streambuf streambuf;
|
||||||
|
};
|
||||||
|
|
||||||
|
class server {
|
||||||
|
public:
|
||||||
|
server(boost::asio::io_context& io_context, std::uint16_t port) :
|
||||||
|
io_context(io_context),
|
||||||
|
acceptor(
|
||||||
|
io_context,
|
||||||
|
boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)) {}
|
||||||
|
|
||||||
|
void async_accept() {
|
||||||
|
socket.emplace(io_context);
|
||||||
|
|
||||||
|
acceptor.async_accept(*socket, [&](boost::system::error_code error) {
|
||||||
|
std::make_shared<session>(std::move(*socket))->start();
|
||||||
|
async_accept();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
boost::asio::io_context& io_context;
|
||||||
|
boost::asio::ip::tcp::acceptor acceptor;
|
||||||
|
std::optional<boost::asio::ip::tcp::socket> socket;
|
||||||
|
};
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
boost::asio::io_context io_context;
|
||||||
|
server srv(io_context, 15001);
|
||||||
|
srv.async_accept();
|
||||||
|
io_context.run();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
131
code/tcp_chat_server.cpp
Normal file
131
code/tcp_chat_server.cpp
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
#include <boost/asio.hpp>
|
||||||
|
#include <optional>
|
||||||
|
#include <queue>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
namespace io = boost::asio;
|
||||||
|
using tcp = io::ip::tcp;
|
||||||
|
using error_code = boost::system::error_code;
|
||||||
|
using namespace std::placeholders;
|
||||||
|
|
||||||
|
using message_handler = std::function<void(std::string)>;
|
||||||
|
using error_handler = std::function<void()>;
|
||||||
|
|
||||||
|
class session: public std::enable_shared_from_this<session> {
|
||||||
|
public:
|
||||||
|
session(tcp::socket&& socket) : socket(std::move(socket)) {}
|
||||||
|
|
||||||
|
void start(message_handler&& on_message, error_handler&& on_error) {
|
||||||
|
this->on_message = std::move(on_message);
|
||||||
|
this->on_error = std::move(on_error);
|
||||||
|
async_read();
|
||||||
|
}
|
||||||
|
|
||||||
|
void post(const std::string& message) {
|
||||||
|
bool idle = outgoing.empty();
|
||||||
|
outgoing.push(message);
|
||||||
|
|
||||||
|
if (idle) {
|
||||||
|
async_write();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void async_read() {
|
||||||
|
io::async_read_until(
|
||||||
|
socket,
|
||||||
|
streambuf,
|
||||||
|
"\n",
|
||||||
|
std::bind(&session::on_read, shared_from_this(), _1, _2));
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_read(error_code error, std::size_t bytes_transferred) {
|
||||||
|
if (!error) {
|
||||||
|
std::stringstream message;
|
||||||
|
message << socket.remote_endpoint(error) << ": "
|
||||||
|
<< std::istream(&streambuf).rdbuf();
|
||||||
|
streambuf.consume(bytes_transferred);
|
||||||
|
on_message(message.str());
|
||||||
|
async_read();
|
||||||
|
} else {
|
||||||
|
socket.close(error);
|
||||||
|
on_error();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void async_write() {
|
||||||
|
io::async_write(
|
||||||
|
socket,
|
||||||
|
io::buffer(outgoing.front()),
|
||||||
|
std::bind(&session::on_write, shared_from_this(), _1, _2));
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_write(error_code error, std::size_t bytes_transferred) {
|
||||||
|
if (!error) {
|
||||||
|
outgoing.pop();
|
||||||
|
|
||||||
|
if (!outgoing.empty()) {
|
||||||
|
async_write();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
socket.close(error);
|
||||||
|
on_error();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tcp::socket socket;
|
||||||
|
io::streambuf streambuf;
|
||||||
|
std::queue<std::string> outgoing;
|
||||||
|
message_handler on_message;
|
||||||
|
error_handler on_error;
|
||||||
|
};
|
||||||
|
|
||||||
|
class server {
|
||||||
|
public:
|
||||||
|
server(io::io_context& io_context, std::uint16_t port) :
|
||||||
|
io_context(io_context),
|
||||||
|
acceptor(io_context, tcp::endpoint(tcp::v4(), port)) {}
|
||||||
|
|
||||||
|
void async_accept() {
|
||||||
|
socket.emplace(io_context);
|
||||||
|
|
||||||
|
acceptor.async_accept(*socket, [&](error_code error) {
|
||||||
|
auto client = std::make_shared<session>(std::move(*socket));
|
||||||
|
client->post("Welcome to chat\n\r");
|
||||||
|
post("We have a newcomer\n\r");
|
||||||
|
|
||||||
|
clients.insert(client);
|
||||||
|
|
||||||
|
client->start(
|
||||||
|
std::bind(&server::post, this, _1),
|
||||||
|
[&, weak = std::weak_ptr(client)] {
|
||||||
|
if (auto shared = weak.lock();
|
||||||
|
shared && clients.erase(shared)) {
|
||||||
|
post("We are one less\n\r");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
async_accept();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void post(const std::string& message) {
|
||||||
|
for (auto& client : clients) {
|
||||||
|
client->post(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
io::io_context& io_context;
|
||||||
|
tcp::acceptor acceptor;
|
||||||
|
std::optional<tcp::socket> socket;
|
||||||
|
std::unordered_set<std::shared_ptr<session>> clients;
|
||||||
|
};
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
io::io_context io_context;
|
||||||
|
server srv(io_context, 15001);
|
||||||
|
srv.async_accept();
|
||||||
|
io_context.run();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user