Metadata-Version: 2.4
Name: aioptcp
Version: 1.0.0
Summary: Persistent TCP (PTCP) session-layer protocol implementation over asyncio
Author: Doctorgth
License: Apache Software License 2.0
Project-URL: Homepage, https://github.com/Doctorgth/PTCP
Project-URL: Repository, https://github.com/Doctorgth/PTCP
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System :: Networking
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.18; extra == "dev"
Dynamic: license-file


---

# aioptcp — Python-реализация протокола APTCP

Библиотека `aioptcp` — это асинхронная реализация сеансового протокола **APTCP** (работающего поверх стандартного транспортного протокола TCP) для среды `asyncio`. 

Протокол **APTCP** разработан для обеспечения непрерывности логического соединения при кратковременных обрывах сети, переключениях между интерфейсами (Wi-Fi/LTE) или смене IP-адресов.

> **Важно по терминологии:** 
> * Сам сетевой протокол называется **APTCP**.
> * Реализующая его библиотека для Python называется `aioptcp` (префикс `aio` означает использование `asyncio`).
> * Внутри кода библиотеки классы используют префикс `PTCP` (например, `PTCPSocket`, `PTCPClient`, `PTCPServer`), так как указание на асинхронность уже вынесено в название самого пакета.

---

## Архитектура и особенности

* **Прозрачность для прикладного кода:** При падении физического TCP-соединения логический сокет переходит в режим ожидания. Данные буферизируются на отправку, а вызовы методов `send()` и `recv()` блокируются, но не вызывают ошибок. После восстановления канала сессия APTCP автоматически возобновляется без потерь данных.
* **Встроенный контроль переполнения (Backpressure):** Ограничение буфера переотправки предотвращает бесконтрольное потребление оперативной памяти. Метод `send()` автоматически приостанавливает выполнение корутины, если лимит буфера превышен.
* **Безопасность возобновления сессий:** Возобновление сессии APTCP авторизуется с помощью подписи HMAC-SHA256 на базе ключа, сгенерированного в процессе первичного обмена по алгоритму Диффи-Хеллмана (2048-bit MODP Group).

---

## Установка

Поместите пакет `aioptcp` в директорию вашего проекта или установите его:

```bash
pip install aioptcp
```

---

## Руководство по использованию (Quick Start)

### 1. Запуск APTCP-сервера

Сервер слушает входящие TCP-подключения, обрабатывает рукопожатия APTCP и предоставляет приложению готовые логические сессии.

```python
import asyncio
from aioptcp import PTCPServer, PTCPSocket

async def handle_client(session: PTCPSocket):
    session_hex = session.session_id.hex()
    print(f"[Сервер] APTCP-сессия {session_hex} успешно установлена.")
    
    try:
        while True:
            # Чтение данных из логического сокета APTCP
            data = await session.recv(1024)
            if not data:
                # Получен пустой байтовый массив — клиент закрыл соединение штатно (EOF)
                print(f"[Сервер] Сессия {session_hex} закрыта клиентом.")
                break
                
            print(f"[Сервер] Получено от {session_hex}: {data.decode(errors='ignore')}")
            
            # Отправка эхо-ответа обратно в сессию
            await session.send(b"Echo: " + data)
            
    except Exception as e:
        print(f"[Сервер] Ошибка в сессии {session_hex}: {e}")
    finally:
        # Корректно закрываем ресурсы сокета
        await session.close()

async def main():
    # Запуск сервера APTCP на порту 8888 с таймаутом удержания сессии 30 секунд
    server = PTCPServer(host='127.0.0.1', port=8888, timeout=30)
    await server.start()
    print("[Сервер] APTCP-сервер запущен и ожидает подключений...")
    
    while True:
        # Ожидание нового логического подключения APTCP
        session = await server.accept()
        asyncio.create_task(handle_client(session))

if __name__ == "__main__":
    asyncio.run(main())
```

### 2. Запуск APTCP-клиента

Клиент инициирует соединение. В случае физического обрыва связи библиотека переподключается в фоновом режиме, при этом прикладной цикл отправки/приема не прерывается.

```python
import asyncio
from aioptcp import PTCPClient

async def main():
    # Создание клиента APTCP с таймаутом удержания сессии 30 секунд
    client = PTCPClient(host='127.0.0.1', port=8888, timeout=30)
    
    try:
        print("[Клиент] Подключение к APTCP-серверу...")
        await client.connect()
        print(f"[Клиент] Логическое соединение установлено. ID сессии: {client.session_id.hex()}")
        
        # Отправка сообщений в цикле
        for i in range(1, 6):
            message = f"Message {i}".encode()
            print(f"[Клиент] Отправка: {message.decode()}")
            
            # Если сеть пропадет, метод send заблокируется, но не упадет с ошибкой
            await client.send(message)
            
            # Ожидание ответа
            response = await client.recv(1024)
            print(f"[Клиент] Ответ от сервера: {response.decode(errors='ignore')}")
            
            await asyncio.sleep(2)
            
    except Exception as e:
        print(f"[Клиент] Критическая ошибка: {e}")
    finally:
        # Штатное закрытие логического сокета и отправка кадра CLOSE
        print("[Клиент] Закрытие соединения.")
        await client.close()

if __name__ == "__main__":
    asyncio.run(main())
```

---

## Справочник по API (API Reference)

### Класс `PTCPSocket`
Базовый класс, реализующий логический сокет протокола APTCP. Используется клиентом напрямую (наследуется в `PTCPClient`) и возвращается методом `PTCPServer.accept()`.

*   **`state: PTCPState`**
    Текущее состояние логического сокета. Значения (IntEnum):
    *   `PTCPState.CONNECTING` (1) — выполняется первичное рукопожатие.
    *   `PTCPState.ESTABLISHED` (2) — соединение установлено, передача разрешена.
    *   `PTCPState.DISCONNECTED_WAITING` (3) — физический канал утерян, ожидание восстановления.
    *   `PTCPState.RESUMING` (4) — выполняется восстановление сессии на новом TCP-канале.
    *   `PTCPState.CLOSED` (5) — соединение закрыто окончательно.
*   **`session_id: bytes`**
    Уникальный 16-байтный идентификатор сессии APTCP. Заполняется после завершения рукопожатия.
*   **`buffer_size_limit: int`**
    Лимит размера буфера переотправки (по умолчанию `5 * 1024 * 1024` байт, или 5 МБ).
*   **`async send(data: bytes) -> bool`**
    Асинхронная отправка данных.
    *   Если буфер переотправки переполнен (размер неотправленных данных $\ge$ `buffer_size_limit`), корутина приостанавливает выполнение (блокируется) до тех пор, пока от противоположной стороны не придет ACK-подтверждение.
    *   Если сокет находится в состоянии `DISCONNECTED_WAITING` или `RESUMING`, данные буферизируются, а корутина завершается успешно.
    *   Возвращает `True` при успешной буферизации/отправке. Возвращает `False`, если сокет окончательно закрыт (`CLOSED`).
*   **`async recv(size: int) -> bytes`**
    Асинхронное чтение данных из прикладного буфера приема.
    *   Блокирует выполнение до появления данных в буфере.
    *   Возвращает полученные данные длиной не более `size` байт.
    *   **Важно:** Возвращает пустую строку байт (`b''`), когда удаленная сторона штатно закрыла соединение (сигнал EOF).
*   **`async close(send_close_frame: bool = True)`**
    Завершает логическую сессию и освобождает системные ресурсы. Если `send_close_frame` равен `True`, отправляет удаленной стороне служебный кадр `CLOSE`.

### Класс `PTCPClient(PTCPSocket)`
Класс клиента протокола APTCP, наследующий логику `PTCPSocket`.

*   **`__init__(host: str, port: int, timeout: int = 30)`**
    Создает экземпляр клиента. `timeout` определяет, в течение какого времени (в секундах) клиент будет пытаться восстановить логическую сессию после обрыва сети.
*   **`async connect()`**
    Инициирует физическое соединение с сервером и производит обмен ключами (рукопожатие Диффи-Хеллмана). Блокирует выполнение до перехода сокета в состояние `ESTABLISHED`. При невозможности установить первичное соединение в течение `timeout` секунд возбуждает `TimeoutError`.

### Класс `PTCPServer`
Класс сервера, управляющий жизненным циклом сессий APTCP.

*   **`__init__(host: str, port: int, timeout: int = 30)`**
    Создает экземпляр сервера. `timeout` — время ожидания переподключения клиента до уничтожения сессии из памяти сервера.
*   **`async start()`**
    Запускает TCP-слушатель на указанном хосте и порту, а также активирует фоновый сборщик мусора (Garbage Collector) для очистки зависших сессий.
*   **`async accept() -> PTCPSocket`**
    Ожидает завершения рукопожатия с новым клиентом и возвращает готовый объект сокета `PTCPSocket` в состоянии `ESTABLISHED`.
*   **`sessions: dict`**
    Словарь активных сессий сервера, где ключ — `session_id` (16 байт), а значение — экземпляр `PTCPSocket`.

---

## Инструкции для ИИ (AI Integration Guidelines)

При написании программного кода, использующего `aioptcp`, руководствуйтесь следующими правилами:

1.  **Не реализуйте логику повторного подключения (reconnect) на уровне приложения.**
    Если произошел сбой сети, **не нужно** заново создавать экземпляр `PTCPClient` и повторно вызывать `connect()`. Библиотека сама переведет сокет в состояние `DISCONNECTED_WAITING` и восстановит физическое TCP-соединение в фоновом режиме. Прикладные вызовы `send()` и `recv()` просто подождут завершения этого процесса.
2.  **Определяйте закрытие сокета по пустому результату чтения.**
    Единственный верный способ узнать, что удаленная сторона закрыла логический сокет — это получить `b''` в качестве результата вызова `await session.recv()`.
3.  **Используйте конкурентные задачи для работы с сервером.**
    Метод `PTCPServer.accept()` вызывается в бесконечном цикле, и каждую полученную сессию необходимо передавать в отдельную корутину с помощью `asyncio.create_task()`, чтобы сервер мог продолжать принимать новые соединения.
4.  **Следите за закрытием ресурсов.**
    Всегда закрывайте сессию с помощью `await session.close()` в блоке `finally` обработчика соединений для предотвращения утечки дескрипторов файлов ОС.
