Skip to content

Commit 0cf7cd2

Browse files
committed
feat: add simple server section
1 parent 8edd47b commit 0cf7cd2

File tree

3 files changed

+140
-8
lines changed

3 files changed

+140
-8
lines changed

TUTORIAL.md

Lines changed: 67 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11

22
# Содержание
33

4-
1. [Введение](#org504f6ba)
5-
2. [TCP и UDP](#orgd89f8a1)
6-
1. [Transmission Control Protocol — TCP](#org0185056)
7-
2. [User Datagram Protocol — UDP](#orga3ceb8c)
4+
1. [Введение](#org82d7079)
5+
2. [TCP и UDP](#org2157ab9)
6+
1. [Transmission Control Protocol — TCP](#orgc5dc4b2)
7+
2. [User Datagram Protocol — UDP](#org1d198a9)
8+
3. [Самый простой сервер](#org23f420d)
89

910

1011

11-
<a id="org504f6ba"></a>
12+
<a id="org82d7079"></a>
1213

1314
# Введение
1415

@@ -29,14 +30,14 @@
2930
На этом вступление окончено. Теперь вы готовы приступить к погружению в сетевое программирование на C++.
3031

3132

32-
<a id="orgd89f8a1"></a>
33+
<a id="org2157ab9"></a>
3334

3435
# TCP и UDP
3536

3637
Существует два основных протокола транспортного уровня, которые мы будем использовать — TCP и UDP. Протокол — это набор соглашений о том, как должны передаваться данные по сети.
3738

3839

39-
<a id="org0185056"></a>
40+
<a id="orgc5dc4b2"></a>
4041

4142
## Transmission Control Protocol — TCP
4243

@@ -48,7 +49,7 @@ TCP-соединение очень похоже на файл: мы откры
4849
Другими словами, файл предоставляет вам произвольный доступ, в то время как TCP-соединение представляет собой двунаправленный последовательный поток.
4950

5051

51-
<a id="orga3ceb8c"></a>
52+
<a id="org1d198a9"></a>
5253

5354
## User Datagram Protocol — UDP
5455

@@ -63,3 +64,61 @@ TCP-соединение очень похоже на файл: мы откры
6364

6465
Это все, что вам необходимо знать о протоколах на данный момент. Значит, мы можем двигаться дальше.
6566

67+
68+
<a id="org23f420d"></a>
69+
70+
# Самый простой сервер
71+
72+
Согласно [Википедии](https://ru.wikipedia.org/wiki/Сервер_(программное_обеспечение)),
73+
74+
> Сервер — программный компонент вычислительной системы, выполняющий сервисные (обслуживающие) функции по запросу клиента, предоставляя ему доступ к определённым ресурсам или услугам.
75+
76+
Это определение очень точно подмечает тот факт, что сервер — это всего лишь приложение, которое получает какие-то данные от других приложений и возвращает некоторые данные обратно.
77+
78+
Мы начнем с самого простого сервера, который приходит на ум — эхо UDP-сервер. Он выполняет следующие действия:
79+
80+
- Получает любые данные, которые были отправлены на UDP-порт 15001.
81+
- Отправляет полученные данные обратно отправителю «как есть».
82+
83+
На самом деле вы можете выбрать практически любой порт для вашего сервера. Существует множество часто используемых портов для различных служб, которые вы можете найти здесь: [Список портов TCP и UDP](https://ru.wikipedia.org/wiki/Список_портов_TCP_и_UDP). Однако, как правило, только несколько из этих служб используется одновременно в недавно установленной ОС.
84+
85+
Теперь давайте взглянем на следующий [исходный код](./code/simple_server.cpp):
86+
87+
#include <boost/asio.hpp>
88+
89+
int main() {
90+
std::uint16_t port = 15001;
91+
92+
boost::asio::io_context io_context;
93+
boost::asio::ip::udp::endpoint receiver(boost::asio::ip::udp::v4(), port);
94+
boost::asio::ip::udp::socket socket(io_context, receiver);
95+
96+
while (true) {
97+
char buffer[65536];
98+
boost::asio::ip::udp::endpoint sender;
99+
std::size_t bytes_transferred =
100+
socket.receive_from(boost::asio::buffer(buffer), sender);
101+
socket.send_to(boost::asio::buffer(buffer, bytes_transferred), sender);
102+
}
103+
104+
return 0;
105+
}
106+
107+
Вам даже не обязательно отдельно скачивать `.cpp` файл сервера, поскольку вышеприведенный код — это полноценный эхо UDP-сервер. Мы не реализовали логирование и обработку ошибок, чтобы код выглядел максимально просто. Об обработке ошибок мы поговорим позднее. Давайте разберемся, что происходит в этом коде:
108+
109+
- `boost::asio::io_context` — основной поставщик услуг ввода-вывода. В данный момент вы можете рассматривать его как исполнителя (executor) запланированных задач. Вы поймете его назначение сразу после того, как мы перейдем к асинхронному потоку управления, что произойдет очень скоро.
110+
- `boost::asio::ip::udp::endpoint` — это пара IP-адреса и порта.
111+
- `boost::asio::ip::udp::socket` — это сокет. Вы можете рассматривать его как дескриптор файла, предназначенный для сетевого взаимодействия. Обычно, когда вы открываете файл, вы получаете дескриптор файла. Когда вы взаимодействуете по сети, вы используете сокет.
112+
- Каждый сокет прикреплен к некоторому `io_context`, а потому каждый сокет конструируется с помощью ссылки на `io_context`. Второй параметр конструктора сокета — `endpoint` — IP-адрес и порт, который используется для получения входящих дейтаграмм (в случае UDP) или соединений (в случае TCP).
113+
- `boost::asio::ip::udp::v4()` возвращает объект, который в данный момент вы должны рассматривать как просто сетевой интерфейс UDP по умолчанию.
114+
- `boost::asio::buffer()` — это представление буфера, которое содержит указатель и размер, причем это представление не владеет памятью. В нашем случае оно указывает на массив `char`.
115+
- `socket::receive_from` ожидает входящий UDP-пакет, заполняет `buffer` полученными данными, а также заполняет `sender` информацией об отправителе, которая также включает в себя пару IP-адреса и порта.
116+
- `socket::send_to` отправляет UDP-пакет, используя данные из представления буфера. Получатель пакета передается вторым аргументом. В нашем случае получателем является отправитель, поскольку речь идет об эхо-сервере.
117+
118+
Итак, мы сделали следующее:
119+
120+
- Создали UDP-сокет и настроили его на ожидание UDP-пакетов на порту 15001.
121+
- Запустили бесконечный цикл, в котором ожидаем входящие UDP-пакеты, а после получения отправляем их обратно отправителю.
122+
123+
Поздравляем! Вы только что создали ваш первый сервер с помощью C++ и Boost.Asio!
124+

code/simple_server.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#include <boost/asio.hpp>
2+
3+
int main() {
4+
std::uint16_t port = 15001;
5+
6+
boost::asio::io_context io_context;
7+
boost::asio::ip::udp::endpoint receiver(boost::asio::ip::udp::v4(), port);
8+
boost::asio::ip::udp::socket socket(io_context, receiver);
9+
10+
while (true) {
11+
char buffer[65536];
12+
boost::asio::ip::udp::endpoint sender;
13+
std::size_t bytes_transferred =
14+
socket.receive_from(boost::asio::buffer(buffer), sender);
15+
socket.send_to(boost::asio::buffer(buffer, bytes_transferred), sender);
16+
}
17+
18+
return 0;
19+
}

tutorial.org

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,57 @@ TCP-соединение очень похоже на файл: мы откры
4040
Как вы можете понять, UDP немного сложнее в использовании, чем TCP. Тем не менее, у UDP есть свои преимущества, которые мы обсудим позднее.
4141

4242
Это все, что вам необходимо знать о протоколах на данный момент. Значит, мы можем двигаться дальше.
43+
44+
* Самый простой сервер
45+
46+
Согласно [[https://ru.wikipedia.org/wiki/Сервер_(программное_обеспечение)][Википедии]],
47+
#+begin_quote
48+
Сервер — программный компонент вычислительной системы, выполняющий сервисные (обслуживающие) функции по запросу клиента, предоставляя ему доступ к определённым ресурсам или услугам.
49+
#+end_quote
50+
51+
Это определение очень точно подмечает тот факт, что сервер — это всего лишь приложение, которое получает какие-то данные от других приложений и возвращает некоторые данные обратно.
52+
53+
Мы начнем с самого простого сервера, который приходит на ум — эхо UDP-сервер. Он выполняет следующие действия:
54+
- Получает любые данные, которые были отправлены на UDP-порт 15001.
55+
- Отправляет полученные данные обратно отправителю «как есть».
56+
57+
На самом деле вы можете выбрать практически любой порт для вашего сервера. Существует множество часто используемых портов для различных служб, которые вы можете найти здесь: [[https://ru.wikipedia.org/wiki/Список_портов_TCP_и_UDP][Список портов TCP и UDP]]. Однако, как правило, только несколько из этих служб используется одновременно в недавно установленной ОС.
58+
59+
Теперь давайте взглянем на следующий [[./code/simple_server.cpp][исходный код]]:
60+
#+begin_src cpp :tangle code/simple_server.cpp
61+
#include <boost/asio.hpp>
62+
63+
int main() {
64+
std::uint16_t port = 15001;
65+
66+
boost::asio::io_context io_context;
67+
boost::asio::ip::udp::endpoint receiver(boost::asio::ip::udp::v4(), port);
68+
boost::asio::ip::udp::socket socket(io_context, receiver);
69+
70+
while (true) {
71+
char buffer[65536];
72+
boost::asio::ip::udp::endpoint sender;
73+
std::size_t bytes_transferred =
74+
socket.receive_from(boost::asio::buffer(buffer), sender);
75+
socket.send_to(boost::asio::buffer(buffer, bytes_transferred), sender);
76+
}
77+
78+
return 0;
79+
}
80+
#+end_src
81+
82+
Вам даже не обязательно отдельно скачивать ~.cpp~ файл сервера, поскольку вышеприведенный код — это полноценный эхо UDP-сервер. Мы не реализовали логирование и обработку ошибок, чтобы код выглядел максимально просто. Об обработке ошибок мы поговорим позднее. Давайте разберемся, что происходит в этом коде:
83+
- ~boost::asio::io_context~ — основной поставщик услуг ввода-вывода. В данный момент вы можете рассматривать его как исполнителя (executor) запланированных задач. Вы поймете его назначение сразу после того, как мы перейдем к асинхронному потоку управления, что произойдет очень скоро.
84+
- ~boost::asio::ip::udp::endpoint~ — это пара IP-адреса и порта.
85+
- ~boost::asio::ip::udp::socket~ — это сокет. Вы можете рассматривать его как дескриптор файла, предназначенный для сетевого взаимодействия. Обычно, когда вы открываете файл, вы получаете дескриптор файла. Когда вы взаимодействуете по сети, вы используете сокет.
86+
- Каждый сокет прикреплен к некоторому ~io_context~, а потому каждый сокет конструируется с помощью ссылки на ~io_context~. Второй параметр конструктора сокета — ~endpoint~ — IP-адрес и порт, который используется для получения входящих дейтаграмм (в случае UDP) или соединений (в случае TCP).
87+
- ~boost::asio::ip::udp::v4()~ возвращает объект, который в данный момент вы должны рассматривать как просто сетевой интерфейс UDP по умолчанию.
88+
- ~boost::asio::buffer()~ — это представление буфера, которое содержит указатель и размер, причем это представление не владеет памятью. В нашем случае оно указывает на массив ~char~.
89+
- ~socket::receive_from~ ожидает входящий UDP-пакет, заполняет ~buffer~ полученными данными, а также заполняет ~sender~ информацией об отправителе, которая также включает в себя пару IP-адреса и порта.
90+
- ~socket::send_to~ отправляет UDP-пакет, используя данные из представления буфера. Получатель пакета передается вторым аргументом. В нашем случае получателем является отправитель, поскольку речь идет об эхо-сервере.
91+
92+
Итак, мы сделали следующее:
93+
- Создали UDP-сокет и настроили его на ожидание UDP-пакетов на порту 15001.
94+
- Запустили бесконечный цикл, в котором ожидаем входящие UDP-пакеты, а после получения отправляем их обратно отправителю.
95+
96+
Поздравляем! Вы только что создали ваш первый сервер с помощью C++ и Boost.Asio!

0 commit comments

Comments
 (0)