> For the complete documentation index, see [llms.txt](https://dh-organization.gitbook.io/droneshub-rover-kontakt/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://dh-organization.gitbook.io/droneshub-rover-kontakt/programmirovanie-rovera-kontakt/tekhnologiya-soketov.md).

# Технология сокетов

Технология сокетов и библиотека socket.io в Python предоставляют мощные средства для реализации сетевого взаимодействия между устройствами. Давайте разберем все по порядку.

**Основы**

Сокет (англ. socket) — это программный интерфейс для обмена данными между процессами по сети. Сокеты позволяют передавать данные между компьютерами, подключенными к сети. Существуют различные типы сокетов, но наиболее часто используются сокеты типа TCP и UDP.

1. **TCP (Transmission Control Protocol):**
   * Надежный, ориентированный на соединение протокол.
   * Гарантирует доставку данных в правильном порядке.
   * Используется для приложений, где важна целостность данных (например, веб-серверы, почтовые серверы).
2. **UDP (User Datagram Protocol):**
   * Протокол без установления соединения.
   * Не гарантирует доставку данных.
   * Менее надежен, но быстрее, чем TCP.
   * Подходит для приложений, где скорость важнее надежности (например, потоковое видео, онлайн-игры).

**Основные операции с сокетами**

* **Создание сокета:** Использование функции `socket()` для создания нового сокета.
* **Связывание с адресом:** Использование функции `bind()` для привязки сокета к определенному адресу и порту.
* **Прослушивание и принятие соединений:** Для TCP-сокетов используется `listen()` и `accept()`.
* **Отправка и получение данных:** Для передачи данных используются функции `send()` и `recv()` для TCP или `sendto()` и `recvfrom()` для UDP.
* **Закрытие сокета:** Использование функции `close()` для закрытия сокета.

#### Socket.IO

Socket.IO — это библиотека, которая позволяет легко и надежно реализовать реальное время взаимодействие между клиентами и серверами. Она поддерживает множество транспортных протоколов, таких как WebSocket, и автоматически выбирает оптимальный для текущей среды.

**Установка**

Для установки Socket.IO в Python используется пакет `python-socketio`:

```sh
pip install python-socketio
```

Для работы на стороне сервера также понадобится `eventlet` или `gevent`:

```sh
pip install eventlet
```

**Основные концепции**

Socket.IO работает по модели событий, где клиент и сервер могут обмениваться событиями и данными. Основные операции включают:

* **Подключение:** Клиент подключается к серверу и открывается сокет.
* **События:** Обмен событиями между клиентом и сервером.
* **Комнаты:** Логические группы для организации подключения клиентов.
* **Разрывы соединений:** Отключение клиентов от сервера.

**Пример использования Socket.IO**

**Серверная часть (Python)**

```python
import socketio
import eventlet

# Создание сервера
sio = socketio.Server()
app = socketio.WSGIApp(sio)

# Функции для управления ровером
def move_forward():
    print("Ровер движется вперед")
    # Здесь добавьте код для управления двигателями ровера через API DroneHub

def turn_left():
    print("Ровер поворачивает налево")
    # Здесь добавьте код для управления двигателями ровера через API DroneHub

def turn_right():
    print("Ровер поворачивает направо")
    # Здесь добавьте код для управления двигателями ровера через API DroneHub

def stop():
    print("Ровер останавливается")
    # Здесь добавьте код для остановки двигателей ровера через API DroneHub

# Обработчик события подключения
@sio.event
def connect(sid, environ):
    print('Подключение:', sid)
    sio.emit('message', 'Добро пожаловать на борт ровера!')

# Обработчик пользовательского события для управления ровером
@sio.event
def control(sid, command):
    print('Команда от', sid, ':', command)
    if command == 'forward':
        move_forward()
    elif command == 'left':
        turn_left()
    elif command == 'right':
        turn_right()
    elif command == 'stop':
        stop()
    else:
        sio.emit('message', 'Неизвестная команда')

# Обработчик события отключения
@sio.event
def disconnect(sid):
    print('Отключение:', sid)

# Запуск сервера
if __name__ == '__main__':
    eventlet.wsgi.server(eventlet.listen(('0.0.0.0', 5000)), app)

```

#### Импорт необходимых библиотек

```python
import socketio
import eventlet
```

* **socketio**: Библиотека для работы с WebSocket, которая позволяет создавать сервер и обрабатывать события.
* **eventlet**: Библиотека для создания асинхронных серверов, используемая здесь для запуска WebSocket-сервера.

#### Создание сервера

```python
# Создание сервера
sio = socketio.Server()
app = socketio.WSGIApp(sio)
```

* **sio = socketio.Server()**: Создает экземпляр сервера Socket.IO.
* **app = socketio.WSGIApp(sio)**: Создает WSGI-приложение, которое будет использоваться для запуска сервера.

#### Функции для управления ровером

```python
# Функции для управления ровером
def move_forward():
    print("Ровер движется вперед")
    # Здесь добавьте код для управления двигателями ровера через API DroneHub

def turn_left():
    print("Ровер поворачивает налево")
    # Здесь добавьте код для управления двигателями ровера через API DroneHub

def turn_right():
    print("Ровер поворачивает направо")
    # Здесь добавьте код для управления двигателями ровера через API DroneHub

def stop():
    print("Ровер останавливается")
    # Здесь добавьте код для остановки двигателей ровера через API DroneHub
```

* **move\_forward()**: Функция для движения ровера вперед.
* **turn\_left()**: Функция для поворота ровера налево.
* **turn\_right()**: Функция для поворота ровера направо.
* **stop()**: Функция для остановки ровера.

#### Обработчик события подключения

```python
# Обработчик события подключения
@sio.event
def connect(sid, environ):
    print('Подключение:', sid)
    sio.emit('message', 'Добро пожаловать на борт ровера!')
```

* **@sio.event**: Декоратор, который регистрирует функцию как обработчик события.
* **connect(sid, environ)**: Обработчик события подключения клиента. `sid` — уникальный идентификатор клиента, `environ` — словарь с информацией о среде.
* **sio.emit('message', 'Добро пожаловать на борт ровера!')**: Отправляет сообщение клиенту при подключении.

#### Обработчик пользовательского события для управления ровером

```python
# Обработчик пользовательского события для управления ровером
@sio.event
def control(sid, command):
    print('Команда от', sid, ':', command)
    if command == 'forward':
        move_forward()
    elif command == 'left':
        turn_left()
    elif command == 'right':
        turn_right()
    elif command == 'stop':
        stop()
    else:
        sio.emit('message', 'Неизвестная команда')
```

* **control(sid, command)**: Обработчик события `control`, который принимает команду от клиента.
* **if command == 'forward':**: Если команда — "forward", вызывается функция `move_forward()`.
* **elif command == 'left':**: Если команда — "left", вызывается функция `turn_left()`.
* **elif command == 'right':**: Если команда — "right", вызывается функция `turn_right()`.
* **elif command == 'stop':**: Если команда — "stop", вызывается функция `stop()`.
* **else: sio.emit('message', 'Неизвестная команда')**: Если команда неизвестна, отправляется сообщение клиенту.

#### Обработчик события отключения

```python
# Обработчик события отключения
@sio.event
def disconnect(sid):
    print('Отключение:', sid)
```

* **disconnect(sid)**: Обработчик события отключения клиента.

#### Запуск сервера

```python
# Запуск сервера
if __name__ == '__main__':
    eventlet.wsgi.server(eventlet.listen(('0.0.0.0', 5000)), app)
```

* **if name == 'main':**: Проверка, что скрипт запущен напрямую (а не импортирован как модуль).
* **eventlet.wsgi.server(eventlet.listen(('0.0.0.0', 5000)), app)**: Запуск сервера на всех доступных IP-адресах (`0.0.0.0`) и порту `5000`.

**Клиентская часть (JavaScript, для браузера)**

```html
<!DOCTYPE html>
<html>
<head>
  <title>Socket.IO Control Rover</title>
  <script src="https://cdn.socket.io/4.0.0/socket.io.min.js"></script>
  <script>
    document.addEventListener('DOMContentLoaded', () => {
      var socket = io('http://localhost:5000');

      socket.on('connect', () => {
        console.log('Подключено к серверу');
        socket.emit('message', 'Привет от клиента');
      });

      socket.on('message', (data) => {
        console.log('Сообщение от сервера:', data);
      });

      socket.on('disconnect', () => {
        console.log('Отключено от сервера');
      });

      function moveForward() {
        socket.emit('control', 'forward');
      }

      function turnLeft() {
        socket.emit('control', 'left');
      }

      function turnRight() {
        socket.emit('control', 'right');
      }

      function stop() {
        socket.emit('control', 'stop');
      }
    });
  </script>
</head>
<body>
  <h1>Управление ровером через Socket.IO</h1>
  <button onclick="moveForward()">Вперед</button>
  <button onclick="turnLeft()">Налево</button>
  <button onclick="turnRight()">Направо</button>
  <button onclick="stop()">Стоп</button>
</body>
</html>

```

#### Объяснение кода:

1. **DOCTYPE и HTML-теги**:
   * `<!DOCTYPE html>`: Объявляет тип документа как HTML5.
   * `<html>`: Корневой элемент HTML-документа.
2. **Заголовок и подключение библиотеки Socket.IO**:
   * `<head>`: Содержит метаданные и скрипты.
   * `<title>Socket.IO Control Rover</title>`: Заголовок страницы.
   * `<script src="https://cdn.socket.io/4.0.0/socket.io.min.js"></script>`: Подключает библиотеку Socket.IO для работы с WebSocket.
3. **Скрипт для управления WebSocket**:
   * `<script>`: Содержит JavaScript-код для управления WebSocket.
   * `document.addEventListener('DOMContentLoaded', () => { ... });`: Ожидает загрузки DOM-структуры перед выполнением скрипта.
   * `var socket = io('http://localhost:5000');`: Создает соединение с сервером по адресу `http://localhost:5000`.
4. **Обработчики событий WebSocket**:
   * `socket.on('connect', () => { ... });`: Выполняется при успешном подключении к серверу.
     * `console.log('Подключено к серверу');`: Выводит сообщение в консоль.
     * `socket.emit('message', 'Привет от клиента');`: Отправляет сообщение "Привет от клиента" на сервер.
   * `socket.on('message', (data) => { ... });`: Выполняется при получении сообщения от сервера.
     * `console.log('Сообщение от сервера:', data);`: Выводит сообщение от сервера в консоль.
   * `socket.on('disconnect', () => { ... });`: Выполняется при отключении от сервера.
     * `console.log('Отключено от сервера');`: Выводит сообщение в консоль.
5. **Функции для отправки команд управления**:
   * `function moveForward() { ... }`: Отправляет команду "forward" на сервер.
   * `function turnLeft() { ... }`: Отправляет команду "left" на сервер.
   * `function turnRight() { ... }`: Отправляет команду "right" на сервер.
   * `function stop() { ... }`: Отправляет команду "stop" на сервер.
6. **Кнопки для управления ровером**:
   * `<button onclick="moveForward()">Вперед</button>`: Кнопка для движения вперед.
   * `<button onclick="turnLeft()">Налево</button>`: Кнопка для поворота налево.
   * `<button onclick="turnRight()">Направо</button>`: Кнопка для поворота направо.
   * `<button onclick="stop()">Стоп</button>`: Кнопка для остановки ровера.

#### Асинхронное использование

Асинхронное программирование позволяет выполнять операции ввода-вывода без блокировки основного потока выполнения, что значительно повышает производительность сетевых приложений.

Socket.IO поддерживает асинхронное программирование с использованием библиотеки `asyncio`.

**Пример асинхронного использования**

**Серверная часть (Python)**

```python
import asyncio
import socketio

# Создание асинхронного сервера
sio = socketio.AsyncServer(async_mode='asgi')
app = socketio.ASGIApp(sio)

# Заглушки для функций управления ровером
async def move_forward():
    print("Ровер движется вперед")
    # Здесь должен быть код для отправки команды на ровер через API DroneHub

async def turn_left():
    print("Ровер поворачивает налево")
    # Здесь должен быть код для отправки команды на ровер через API DroneHub

async def turn_right():
    print("Ровер поворачивает направо")
    # Здесь должен быть код для отправки команды на ровер через API DroneHub

async def stop():
    print("Ровер останавливается")
    # Здесь должен быть код для отправки команды на ровер через API DroneHub

# Обработчик события подключения
@sio.event
async def connect(sid, environ):
    print('Подключение:', sid)

# Обработчик пользовательского события
@sio.event
async def message(sid, data):
    print('Сообщение от', sid, ':', data)
    await sio.send(sid, 'Ответ от сервера')

# Обработчик события отключения
@sio.event
async def disconnect(sid):
    print('Отключение:', sid)

# Обработчик события управления ровером
@sio.event
async def control(sid, command):
    print('Команда от', sid, ':', command)
    if command == 'forward':
        await move_forward()
    elif command == 'left':
        await turn_left()
    elif command == 'right':
        await turn_right()
    elif command == 'stop':
        await stop()
    else:
        print('Неизвестная команда:', command)

# Запуск сервера
if __name__ == '__main__':
    import uvicorn
    uvicorn.run(app, host='0.0.0.0', port=5000)

```

#### Импорт необходимых модулей

```python
import asyncio
import socketio
```

* `asyncio`: Модуль для асинхронного программирования в Python.
* `socketio`: Библиотека для работы с WebSocket через Socket.IO.

#### Создание асинхронного сервера

```python
sio = socketio.AsyncServer(async_mode='asgi')
app = socketio.ASGIApp(sio)
```

* `socketio.AsyncServer(async_mode='asgi')`: Создает асинхронный сервер Socket.IO с использованием ASGI (Asynchronous Server Gateway Interface).
* `socketio.ASGIApp(sio)`: Создает ASGI-приложение, которое будет использоваться для запуска сервера.

#### Заглушки для функций управления ровером

```python
async def move_forward():
    print("Ровер движется вперед")
    # Здесь должен быть код для отправки команды на ровер через API DroneHub

async def turn_left():
    print("Ровер поворачивает налево")
    # Здесь должен быть код для отправки команды на ровер через API DroneHub

async def turn_right():
    print("Ровер поворачивает направо")
    # Здесь должен быть код для отправки команды на ровер через API DroneHub

async def stop():
    print("Ровер останавливается")
    # Здесь должен быть код для отправки команды на ровер через API DroneHub
```

* Эти функции являются заглушками для управления ровером. В реальном приложении здесь должен быть код для отправки команд на ровер через API DroneHub.

#### Обработчик события подключения

```python
@sio.event
async def connect(sid, environ):
    print('Подключение:', sid)
```

* `@sio.event`: Декоратор, который регистрирует функцию как обработчик события.
* `connect(sid, environ)`: Обработчик события подключения клиента. `sid` — уникальный идентификатор сессии клиента, `environ` — словарь с информацией о среде.

#### Обработчик пользовательского события

```python
@sio.event
async def message(sid, data):
    print('Сообщение от', sid, ':', data)
    await sio.send(sid, 'Ответ от сервера')
```

* `message(sid, data)`: Обработчик события `message`, который принимает сообщения от клиента. `data` — данные, переданные клиентом.
* `await sio.send(sid, 'Ответ от сервера')`: Отправляет ответ клиенту.

#### Обработчик события отключения

```python
@sio.event
async def disconnect(sid):
    print('Отключение:', sid)
```

* `disconnect(sid)`: Обработчик события отключения клиента.

#### Обработчик события управления ровером

```python
@sio.event
async def control(sid, command):
    print('Команда от', sid, ':', command)
    if command == 'forward':
        await move_forward()
    elif command == 'left':
        await turn_left()
    elif command == 'right':
        await turn_right()
    elif command == 'stop':
        await stop()
    else:
        print('Неизвестная команда:', command)
```

* `control(sid, command)`: Обработчик события `control`, который принимает команды управления ровером.
* В зависимости от значения `command`, вызывается соответствующая функция управления ровером.

#### Запуск сервера

```python
if __name__ == '__main__':
    import uvicorn
    uvicorn.run(app, host='0.0.0.0', port=5000)
```

* `if __name__ == '__main__':`: Проверка, чтобы код выполнялся только при запуске скрипта напрямую, а не при импорте.
* `uvicorn.run(app, host='0.0.0.0', port=5000)`: Запускает сервер с использованием Uvicorn на всех доступных интерфейсах (`0.0.0.0`) и порту `5000`.

Этот пример использует `uvicorn` для запуска ASGI приложения.

#### Заключение

Socket.IO в сочетании с асинхронными возможностями Python позволяет легко создавать мощные и производительные сетевые приложения. Основные преимущества включают простоту использования, поддержку различных транспортных протоколов и модель событий, упрощающую разработку приложений реального времени.
