# 2022: Компьютерные системы и сети. Лекция 3.
[TOC]
## Стек протоколов OSI
### Уровни модели OSI
```csvpreview {header="true"}
Модель, Уровень (layer), Тип данных (PDU), Функции, Примеры, Оборудование
Host layers, 7. Прикладной (application), Данные, Доступ к сетевым службам, HTTP FTP POP3 WebSocket, Хосты (клиенты сети), Межсетевой экран
Host layers, 6. Представления (presentation), Данные, Представление и шифрование данных, ASCII EBCDIC JPEG MIDI, Хосты (клиенты сети)
Host layers, 5. Сеансовый (session), Данные, Управление сеансом связи, RPC PAP L2TP gRPC, Хосты (клиенты сети)
Host layers, 4. Транспортный (transport), Сегменты (segment), Датаграммы (datagram), Прямая связь между конечными пунктами и надёжность, TCP UDP SCTP Порты, Хосты
Media layers, 3. Сетевой (network), Пакеты (packet), Определение маршрута и логическая адресация, IPv4 IPv6 IPsec AppleTalk ICMP, Маршрутизатор; Сетевой шлюз; Межсетевой экран
Media layers, 2. Канальный (data link), Биты (bit)/ Кадры (frame), Физическая адресация, PPP IEEE 802.22 Ethernet DSL ARP, сетевая карта; Сетевой мост; Коммутатор; точка доступа
Media layers, 1. Физический (physical), Биты (bit), Работа со средой передачи; Работа c сигналами и двоичными данными, USB; RJ («витая пара»; коаксиальный; оптоволоконный); радиоканал, Концентратор; Повторитель (сетевое оборудование)
```
В литературе наиболее часто принято начинать описание уровней модели OSI с 7-го уровня, называемого прикладным, на котором пользовательские приложения обращаются к сети. Модель OSI заканчивается 1-м уровнем — физическим, на котором определены стандарты, предъявляемые независимыми производителями к средам передачи данных.
## Протокол управления передачей (TCP, Transmission Control Protocol)
Это один из основных протоколов передачи данных интернета. Предназначен для управления передачей данных интернета. Пакеты в TCP называются сегментами.
В стеке протоколов TCP/IP выполняет функции транспортного уровня [модели OSI](https://en.wikipedia.org/wiki/OSI_model#:~:text=in%20this%20model.-,OSI%20model,and%20reception%20of%20raw%20bit%20streams%20over%20a%20physical%20medium,-Layer%201%3A%20Physical).
([См.таблицу](https://ru.wikipedia.org/wiki/%D0%A1%D0%B5%D1%82%D0%B5%D0%B2%D0%B0%D1%8F_%D0%BC%D0%BE%D0%B4%D0%B5%D0%BB%D1%8C_OSI#:~:text=%D0%A3%D1%80%D0%BE%D0%B2%D0%BD%D0%B8%20%D0%BC%D0%BE%D0%B4%D0%B5%D0%BB%D0%B8%20OSI%5B%D0%BF%D1%80%D0%B0%D0%B2%D0%B8%D1%82%D1%8C,%D0%9F%D0%BE%D0%B2%D1%82%D0%BE%D1%80%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%20(%D1%81%D0%B5%D1%82%D0%B5%D0%B2%D0%BE%D0%B5%20%D0%BE%D0%B1%D0%BE%D1%80%D1%83%D0%B4%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5)))
Механизм TCP предоставляет поток данных с [предварительной установкой соединения](https://hpbn.co/building-blocks-of-tcp/#:~:text=%C2%A7-,Three%2DWay%20Handshake,Figure%202%2D1.%20Three%2Dway%20handshake,-Once%20the%20three),

осуществляет повторный запрос данных в случае потери данных и устраняет дублирование при получении двух копий одного пакета, гарантируя тем самым (в отличие от UDP) целостность передаваемых данных и уведомление отправителя о результатах передачи.
Реализации TCP обычно встроены в ядра ОС. Существуют реализации TCP, работающие в пространстве пользователя.
Когда осуществляется передача от компьютера к компьютеру через интернет, TCP работает на верхнем уровне между двумя конечными системами, например, браузером и веб-сервером. TCP осуществляет надёжную передачу потока байтов от одного процесса к другому. При этом TCP осуществляет управление передачей данных.
### Управление передачей данных
#### Управление потоком
Задача, решаемая методами управления потоком, - отправитель информации не должен переполнить буферы приемника.
#### Контроль перегрузки
Задача, решаемая методами контроля перегрузки, - обеспечение нормального (ненасыщенного) состояния канала связи.
Управление передачей при изменяющейся во времени пропускной способности
выполняется множеством алгоритмов.
Отслеживаемыми параметрами этих алгоритмов являются **задержка** (Delay=D или Latency) передачи между терминальными узлами соединения и текущее значение **пропускной способности канала** (Bandwidth=B).

Важнейшей информацией для оптимизации является **BDP= B*D** (Bandwidth-Delay Product), которую можно назвать **объемом канала** (информационной наполняемостью). От этого параметра, в частности, зависит размер буфера данных приемника.

Если источник данных или приемник вынужден часто останавливаться и ожидать получение подтверждения (ACK) для ранее отосланных пакетов данных, то появляются перерывы в потоке данных (data gaps), которые ограничивают реальную полезную пропускную способность канала с обратной связью.
*Задание
Пусть нам известны стабильные полоса пропускания $B$ и задержка $D$ в канале связи. Для надежной передачи блока информации объема $I$ понадобится время $T$, вычисляемое по формуле:
$$
1)\, T = D + \frac{I}{B} \\
2)\, T = \max (2D,\frac{I}{B})\\
3)\, T = \min (D,\frac{I}{2B})
$$
Выберите и обоснуйте правильную формулу.*
В реальности, параметры $B$ и задержки $D$ заранее точно не известны и меняются в зависимости от количества запросов и просто в течение времени.
Чтобы оптимизировать реальную пропускную способность, окно (контрольный размер) отсылаемых данных (cwnd) следует максимально увеличивать, так чтобы потоки отсылки данных и получения подтверждения шли непрерывно по дуплексному каналу.
Оптимальное значение окна (cwnd) зависит от кругового времени путешествия (rtt) по дуплексному каналу. При выборе небольшого cwnd будет ограничена реальная **полезная пропускная способность** (goodput), независимо от доступной физической пропускной способности (Bandwidth). Однако, при превышении оптимального размера окна произойдет **перегрузка** (congestion) канала.

Поэтому целью алгоритмов контроля перегрузки канала является оптимизация (максимизация) окна передачи данных (cwnd) и при этом отслеживания явления перегрузки канала с последующей коррекцией окна и параметров его роста.
## Протокол WebSocket
WebSocket - это высокоуровневый интернет-протокол передачи сообщений (данных) по *двунаправленным* (полнодуплексным) коммуникационным каналам, использующими TCP соединения. Протокол WebSocket был стандартизирован комитетом IETF как RFC 6455 в 2011 году. WebSocket добавляет минимальный функционал поверх TCP:
* модель безопасности, основанная на информации о конечных узлах соединения
* использование URL адресации вместо чистого IP протокола в TCP
* протокол обмена сообщениями поверх потока байтов TCP
* протокол закрытия соединения
### Инициация соединения WebSocket (Protocol handshake)
Инициация соединения стартует со специального клиентского HTTP-запроса и серверного ответа. Такой подход позволяет серверам работать как с HTTP-, так и WebSocket-соединениями на одном и том же порту.
### Пример HTTP-фазы инициализации протокола WebSocket
#### Пример клиентского HTTP запроса на установку WebSocket соединения
(также, как и для чистого HTTP, каждая строка должна завершаться парой байт \r\n и в конце запроса должна быть пустая строка):
```http=
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com
```
#### Ответ сервера:
```http=
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat
```
Как только соединение установлено, коммуникация и передача данных переключается на двунаправленный бинарно-текстовый протокол, который уже никак не связан с HTTP-протоколом.

Рис. Сравнение диаграмм обмена сообщениями для протоколов [HTTP](https://hpbn.co/brief-history-of-http/#:~:text=The%20Hypertext%20Transfer,and%20hardware%20application.), [SSE](https://hpbn.co/server-sent-events-sse/#:~:text=Submit%20Feedback-,Server%2DSent%20Events%20(SSE),-BROWSER%20APIS%20AND) и [WebSocket](https://hpbn.co/websocket/#:~:text=Submit%20Feedback-,WebSocket,-BROWSER%20APIS%20AND)
### Пример клиентского кода WebSocket
```js=
// Creates new WebSocket object with a ws URI as the parameter
const socket = new WebSocket('ws://game.example.com:12010/updates');
// Fired when a connection with a WebSocket is opened,
socket.onopen = () => {
setInterval(() => {
if (socket.bufferedAmount == 0)
socket.send(getUpdateData());
},
50);
};
// Fired when data is received through a WebSocket,
socket.onmessage = event => { handleUpdateData(event.data) };
// Fired when a connection with a WebSocket is closed,
socket.onclose = event => { onSocketClose(event)};
// Fired when a connection has been closed because of an error,
socket.onerror = event =>{ onSocketError(event)};
```
### [Пример тестовой реализации работы WebSocket](https://replit.com/join/byvoqjthld-arthmax)
Код серверной части из этого примера:
```js=
/* Из модуля Express здесь используется только одна функция
- автоматическая отдача клиенту файлов из папки client
В частности, на первый GET / запрос от браузера,
клиенту будет отгружен файл index.html
*/
var express = require('express');
var app = express();
app.use(express.static('client'));
/*
В файле index.html, работающем на стороне клиента,
есть запрос wss://websocket-server.arthmax.repl.co/ по протоколу WebSocket.
Первая фаза этого запроса включает "рукопожатие" по протоколу HTTP.
Именно поэтому в коде подключается модуль http и инициализируется
http сервер, декорированный express-приложением.
А затем подключается модуль ws и его сервер,
который инициализируется только-что созданным http-сервером.
*/
let server = require('http').createServer(app),
WebSocketServer = require('ws').Server,
wss = new WebSocketServer({ server: server });
// После "рукопожатия" ws и http-сервера работают независимо.
// Удобно хранить клиентов в структуре данных Map.
let webSockets = new Map();
// Поскольку встроенного механизма нумерации соединений в WebSocket нет,
// мы будем нумеровать соединения сами.
let new_id = 0;
// Обработчик connection(socket) нового соединения
// должен содержать в себе обработчики событий от клиентского сокета.
wss.on('connection',
function connection(socket) {
// начальная обработка при подключении
socket.id = new_id++;
webSockets.set(socket, socket.id);
socket.send(` Your id=${socket.id} `);
let repl_mess=`A new socket connected with id=${socket.id} `
console.dir(webSockets.values());
for (let w of webSockets.keys()){w.send(repl_mess);}
// обработчики стандартных событий
socket.on('message',
function(mess) {
socket.send(` from Client round trip message echoing from Server at ${new Date().toLocaleString()} "${mess}"`);
});
socket.on('close',
(code) =>{
let close_mess= `closing socket id=${socket.id} code=${code} `
console.log(close_mess);
for (let w of webSockets.keys()){w.send(close_mess);};
webSockets.delete(socket)
console.dir(webSockets.values());
});
});
// Старт работы http-сервера на доступном порту процесса
server.listen(process.env.PORT, function () {
console.log(`Started at ${new Date().toLocaleString()}, Your app is listening on port ${server.address().port}`);
});
```
Фрагмент кода - обработчика принимаемых сообщений, определяющего тип получаемых данных по протоколу WebSocket:
```js=
ws.onmessage = function(msg){
if(msg.data instanceof Blob)processBlob(msg.data);
else processText(msg.data);
}
```
Фрагмент кода, задающего и контролирующего тип бинарных данных:
```js=
var ws = new WebSocket('wss://example.com/socket');
ws.binaryType = "arraybuffer";
ws.onmessage = function(msg) {
if(msg.data instanceof ArrayBuffer)
processArrayBuffer(msg.data);
else
processText(msg.data);
}
```
Фрагмент кода, выполняющего жесткий контроль буфера отправляемых данных, перед отправкой новых данных:
```js=
if(ws.bufferedAmount == 0 ) ws.send(data);
```
Ссылка на реализацию динамического интернет-приложения, использующего протокол WebSocket:
https://glitch.com/edit/#!/volkov-game?path=server.js%3A1%3A31
## Введение в библиотеку Socket.IO
Библиотека Socket.IO - это клиентское и серверное API, инкапсулирующее функционал и возможности протокола [WebSocket](https://en.wikipedia.org/wiki/WebSocket) и обеспечивающее безшовное переключение на протокол XHR в случае невозможности работы по протоколу поддерживать WebSocket.
Подобно node.js, Socket.IO событийно-ориентированная библиотека.
### Отсылка и получение сообщений на клиенте и на сервере
Пример серверного кода, использующего socket.IO
```js=
const express = require("express"); // use express module
const app = express(); // create instance of express
const http = require("http"); // use http module
const server = http.Server(app); // create http server enriched by express functions
const socketio = require("socket.io"); // use socket.io module!
const io = socketio(server); // create instance of socketio over http server
// use "public" directory for static files
app.use(express.static("public"));
io.on("connection",
socket => {
// when server recieves the "joined" message
socket.on("joined",
// send message to client
() => {io.emit("joined")});
// when someone closes the tab
socket.on("disconnect",
() => {io.emit("leave")});
});
server.listen(3000); // run server on port 3000
```
Пример клиентского (браузерного) кода socket.IO
```js=
<!-- use socket.io library -->
<script src="/socket.io/socket.io.js"></script>
<script>
const socket = io(); // create new instance
// when server tells client that someone has opened the page
socket.on("joined", () => { console.log("someone joined");});
// when server tells client that someone has closed the page
socket.on("leave", () => {console.log("someone closed page");});
// tell server that someone opened the page
socket.emit("joined");
</script>
```
### Фрагменты, иллюстрирующие простейшие возможности socket.IO API
#### Сообщения с одним параметром
Передача с клиента на сервер сообщения с одним параметром:
```js
socket.emit("number", 10);
```
Передача с сервера клиентам сообщения с одним параметром:
```js
io.emit("number", 10);
```
Прием сообщения с одним параметром (на клиенте или на сервере):
```js
socket.on("number", number => {
// do something here
});
```
#### Сообщения с множеством параметров
Передача сообщения с множеством параметров:
```js
// send message
socket.emit("numbers", 1, 2, 3);
```
Прием сообщения с множеством параметров:
```js
// recieve message
socket.on("numbers",
(x, y, z) => {
// do something with the x, y, z variables
});
```
### [Сравнение WebSocket и Socket.IO](https://habr.com/ru/post/498996/)
#### Преимущества Socket.IO
* В отличие от веб-сокетов, Socket.IO позволяет отправлять сообщения всем подключенным клиентам. Например, вы пишете чат и хотите уведомлять всех пользователей о подключении нового пользователя. Вы легко можете это реализовать с помощью одной операции. При использовании веб-сокетов, для реализации подобной задачи вам потребуется список подключенных клиентов и отправка сообщений по одному.
* В веб-сокетах сложно использовать [проксирование](https://ru.wikipedia.org/wiki/%D0%9F%D1%80%D0%BE%D0%BA%D1%81%D0%B8-%D1%81%D0%B5%D1%80%D0%B2%D0%B5%D1%80) и [балансировщики нагрузки](https://en.wikipedia.org/wiki/Load_balancing_(computing)). Socket.IO поддерживает [прокси]((https://socket.io/docs/v3/reverse-proxy/)) и [балансировку](https://socket.io/docs/v4/using-multiple-nodes/) из коробки.
* Socket.IO поддерживает постепенную (изящную) деградацию работы каналов при неконтролируемых изменениях состояния сети.
* Socket.IO поддерживает автоматическое переподключение при разрыве соединения.
* Библиотека Socket.IO позволяет писать более короткий код.
#### Преимущества WebSocket
* WebSocket являются стандартным современным протоколом интернет и его API поддерживается всеми современными браузерами.
* Более короткий сетевой траффик в фазе установления соединения.
Веб-сокеты отправляют всего два запроса:
```text
HTTP GET (для получения HTML страницы),
HTTP UPGRADE (для соединения с веб-сокетами)
```
Это позволяет установить соединение с сервером.
Socket.IO в фазе установления соединения отправляет:
```text
HTTP GET (для получения HTML страницы)
Socket.IO client lib (>200кб)
XHR 3 long polling requests
HTTP UPGRADE (для соединения с веб-сокетами)
```
# Задание на лабораторную работу №2
1. Создать клиент-серверное приложение, использующее хостинги:
а) glitch.com,
б) replit.com,
которое обеспечивает работу node.js сервера и клиентской страницы с реализацией функционала на коде библиотек по свободному выбору: 1) WebSocket 2) socket.IO
**Проводится контест на максимально быструю круговую передачу 1 МB информации для каналов и серверов с заранее не известными ограничениями.**
2. Функции, которые должны быть реализованы на html странице:
а) Приветствие посетителя и указание имени, фамилии создателя страницы.
б) 10-кратный тест на максимально быструю круговую передачу 1 МB информации.
Например,
```jsonld=
Вас приветствует html-страница тестирования RTT по протоколу WebSocket
Автор страницы: maximov arthur leonidovich
Нажмите на кнопочку для тестирования кругового времени передачи сообщения
длиной 1 MB
min RTT =
avg RTT =
max RTT =
stddev RTT =
```
# Ресурсы
1. https://hpbn.co/ High Performance Browser Networking, Ilya Grigorik, online version
2. https://en.wikipedia.org/wiki/Transmission_Control_Protocol Transmission Control Protocol
3. https://replit.com/talk/learn/SocketIO-Tutorial-What-its-for-and-how-to-use/143781 Socket.IO Tutorial
4. https://en.wikipedia.org/wiki/WebSocket Протокол WebSocket
5. https://replit.com/join/bscsfctxby-arthmax Пример реализации echo-сервера на библиотеке Socket.IO
6. https://glitch.com/edit/#!/volkov-game?path=server.js%3A1%3A31 Пример интернет-приложения, использующего протокол WebSocket
7. https://replit.com/join/byvoqjthld-arthmax Пример тестовой реализации работы WebSocket
8. [Cсылка на реализацию асинхронной оболочки для socket.emit()](https://medium.com/@nikolozz/using-socket-io-with-async-await-13fa8c2dc9d9)
9. [Emitting event on socketio using async await](https://iqcode.com/code/javascript/emitting-event-on-socketio-using-async-await)
10. [SocketIO acknowledgements](https://socket.io/docs/v4/emitting-events/#acknowledgements)
11. [SocketIO Client Initialization](https://socket.io/docs/v4/client-initialization/)
12. [SocketCluster.Highly scalable pub/sub and RPC framework optimized for async/await](https://socketcluster.io/)
13. [uwebsockets](https://www.npmjs.com/package/uwebsockets.js)