Shanginre / SynchClientServer-library

Внешняя Native API компонента для 1C 8.3, реализующая синхронный TCP клиент-сервер
MIT License
17 stars 2 forks source link
1c 1c-enterprise boost tcp-client tcp-server

SynchClientServer library

Platform Platform License GitHub release (latest SemVer)

Внешняя Native API компонента для 1C 8.3, которая реализует синхронный TCP сервер. Основное назначение компоненты - работа в режиме TCP сервера, но она также может использоваться как TCP клиент. Компонента реализована на C++ с использованием библиотек Boost. Работа с сокетами реализована с использованием библиотеки Boost.Asio.

Методы сервера реализованы в синхронном, а не в асинхронном варианте, чтобы упростить отладку и доработку компоненты. Библиотека Boost.Asio выбрана поскольку она сочетает в себе максимальную гибкость работы с сокетами, производительность и надежность. Так же в Boost.Asio заложены возможности использования не только TCP сокетов, но также UDP-сокетов и COM-портов, что позволяет с минимальными изменениями в структуре кода адаптировать компоненту под специфику своей задачи.

Сервер TCP может работать с произвольным количество соединений как на одном, так и на разных портах (что не позволяет делать стандартная MSWINSCK.OCX от Microsoft). Компонента так же реализует много фич, которых нет в MSWINSCK.OCX. Например, в зависимости от сценария для каждого порта можно настраивать различные параметры:

Компонента протестирована в ходе множества реальных внедрений и стабильно работает с большим количеством подключений и интенсивным трафиком сообщений. Основным сценарием использования компоненты в продакшене была интеграция медицинского оборудования и лабораторной информационной системой на платформе 1С.

Поддерживаемые системы

Список методов

SetServerParameters(ПараметрыКомпонентыСервераJson)

Задает параметры работы сервера, но не запускает его. Если параметры сервера указаны не корректно, то выдается ошибка.

Параметры:

Listen()

Запускает сервер TCP. На каждом порте сервера, где isLinkToRemoteServer = ЛОЖЬ, открываются сокеты, которые ждут подключений клиентов. Если на каком-то порте сокет уже открыт другим сервером, тогда сервер не стартует, а возвращается строка JSON с описанием ошибки.

GetMessagesFromClientsWhenArrive(ВходящиеСообщенияJson, ЧастотаПроверки_мс, Таймаут_мс)

Записывает в переменную ВходящиеСообщенияJson строку JSON с данными о принятых сообщениях, которые не были еще обработаны. Функция возвращается ИСТИНА, если есть входящие сообщения или по истечение времени Таймаут_мс. Функция предназначена для использования в качестве условия в цикле, например:

ВходящиеСообщенияJson = ""; 
Пока КомпонентаСервер.GetMessagesFromClientsWhenArrive(ВходящиеСообщенияJson, ЧастотаПроверки_мс, Таймаут_мс) Цикл
    ...
КонецЦикла;

Параметры:

GetMessagesFromClients(ВходящиеСообщенияJson)

Записывает в переменную ВходящиеСообщенияJson строку JSON с данными о принятых сообщениях, которые не были еще обработаны. Функция возвращается ИСТИНА, если есть входящие сообщения. Функция предназначена для периодического вызова, например со стороны клиента 1С через обработчик ожидания. Нужно отметить, что функция GetMessagesFromClients выполняется значительно быстрее, чем функция GetMessagesFromClientsWhenArrive, но ее использование как условия в цикле "Пока" крайне нежелательно, так как это приведет к возрастанию загрузки процессора.

Параметры:

AckMessagesReceipt(МассивНомеровПринятыхСообщенийJson)

Передает компоненте, GUID принятых сообщений, чтобы они были помечены как обработанные. Обработанные сообщения не смогут быть вновь получены методами GetMessagesFromClientsWhenArrive и GetMessagesFromClients. В дальнейшем обработанные сообщения будут асинхронно удалены из памяти компоненты заданием, выполняемым с периодичностью memoryCleaningFrequency.

Параметры:

SendMessageToClient(СообщениеJson)

Передает компоненте сообщение для последующей отправки клиенту, подключенному к сокету сервера. Исходящие сообщения в дальнейшем будут обработаны и отправлены асинхронно в отдельном потоке.

Параметры:

SendMessageToRemoteServer(СообщениеJson)

Передает компоненте сообщение для последующей отправки в сокет, открытый удаленным сервером. Исходящие сообщения в дальнейшем будут обработаны и отправлены асинхронно в отдельном потоке.

Параметры:

GetClientsState()

Возращает информацию о текущем состоянии сокетов на портах.

Возвращаемое значение (строка JSON):

GetLastLogRecords(КоличествоЗаписей, ТолькоНовые)

Получает записи логов сервера. Полученные записи можно сохранить в регистре сведений для удобства просмотра.

Параметры:

Возвращаемое значение (строка JSON):

StopServer()

Останавливает сервер и закрывает все сокеты.

SendTerminationSignalToRunningInstanceOfServer()

Отправляет сигнал останова работающему экземпляру сервера, если такой существует. Сигнал останова задается в параметре serverTerminationSignal. После получения сиглала останова сервер закрывает сокеты на всех портах и завершает все активные потоки. Если экземпляр сервера был запущен как фоновое задание, то фоновое задание завершается.

Примеры

В файле _SynchClientServer.cfe, прикрепленном к релизу, находится демонстрационная конфигурация 1C 8.3, содержащая примеры методов работы с компонентой.

Работа компоненты на стороне сервера 1С в фоновом задании.

Процедура ЗапуститьКомпонентуВТекущемПотоке() Экспорт

    ПараметрыЗапущеннойКомпоненты = ЗапуститьКомпоненту();

    Если ПараметрыЗапущеннойКомпоненты <> Неопределено Тогда 
        ЗапуститьЦиклОбработкиСообщенийСервера(
            ПараметрыЗапущеннойКомпоненты.ВнешняяКомпонента, 
            ПараметрыЗапущеннойКомпоненты.ПараметрыРаботыКомпоненты);
    КонецЕсли;

КонецПроцедуры

Процедура ЗапуститьЦиклОбработкиСообщенийСервера(ВнешняяКомпонента, ПараметрыРаботыКомпоненты)

    ПараметрыОбновленияСлужебнойИнформации = ПараметрыОбновленияСлужебнойИнформацииОРаботеКомпоненты(); 

    ЧастотаПроверки_мс = 1000; 
    Таймаут_мс = 10000;

    ВходящиеСообщенияJson = "";     
    Пока ВнешняяКомпонента.GetMessagesFromClientsWhenArrive(ВходящиеСообщенияJson, ЧастотаПроверки_мс, Таймаут_мс) Цикл
        Если ЗначениеЗаполнено(ВходящиеСообщенияJson) Тогда
            ВходящиеСообщенияСтруктура = СтруктураИзСтрокиJson(
                ВходящиеСообщенияJson, Истина);
            ВходящиеСообщенияJson = "";

            Если ЗначениеЗаполнено(ВходящиеСообщенияСтруктура) Тогда            
                ПодтвердитьПолучениеСообщений(
                    ВнешняяКомпонента, ВходящиеСообщенияСтруктура);

                ОбработатьВходящиеСообщения(
                    ВнешняяКомпонента, 
                    ВходящиеСообщенияСтруктура, 
                    ПараметрыРаботыКомпоненты);
            КонецЕсли;

        КонецЕсли;

        ОбновитьСлужебнуюИнформациюОРаботеКомпонентыВБазе(
            ВнешняяКомпонента, ПараметрыОбновленияСлужебнойИнформации);
    КонецЦикла;

КонецПроцедуры

Работа компоненты на стороне клиента 1С.

&НаКлиенте
Процедура ЗапуститьКомпонентуНаКлиенте(Команда)

    ПараметрыЗапущеннойКомпоненты = бит_КомпонентаSynchClientServerКлиентСервер.ЗапуститьКомпоненту();  

    Если ПараметрыЗапущеннойКомпоненты <> Неопределено Тогда
        ПодключитьОбработчикОжидания("ОбработатьНовыеСообщения", 1);
    Иначе
        ОтключитьОбработчикОжидания("ОбработатьНовыеСообщения");
    КонецЕсли;

КонецПроцедуры

&НаКлиенте
Процедура ОбработатьНовыеСообщения() Экспорт

    бит_КомпонентаSynchClientServerКлиентСервер.ОбработатьНовыеСообщения(
        ПараметрыЗапущеннойКомпоненты.ВнешняяКомпонента, 
        ПараметрыЗапущеннойКомпоненты.ПараметрыРаботыКомпоненты);

КонецПроцедуры

Процедура ОбработатьНовыеСообщения(ВнешняяКомпонента, ПараметрыРаботыКомпоненты) Экспорт

    ВходящиеСообщенияJson = "";
    ВнешняяКомпонента.GetMessagesFromClients(ВходящиеСообщенияJson);

    Если ЗначениеЗаполнено(ВходящиеСообщенияJson) Тогда
        ВходящиеСообщенияСтруктура = СтруктураИзСтрокиJson(
            ВходящиеСообщенияJson, Истина);

        Если ЗначениеЗаполнено(ВходящиеСообщенияСтруктура) Тогда            
            ПодтвердитьПолучениеСообщений(
                ВнешняяКомпонента, ВходящиеСообщенияСтруктура);

            ОбработатьВходящиеСообщения(
                ВнешняяКомпонента, 
                ВходящиеСообщенияСтруктура, 
                ПараметрыРаботыКомпоненты);
        КонецЕсли;
    КонецЕсли;

    ВходящиеСообщенияJson = "";

КонецПроцедуры

Установка в конфигурацию

  1. Скачать архив с релизом компоненты.
  2. Загрузить в конфигурацию в качестве общего макета с двоичными данными

Разворачивание окружения разработки на Windows

  1. Установить Visual Studio 2019. При установке выбрать среди обязательных компонентов также Windows SDK 10.
  2. Скачать исходники репозитория данного проекта.
  3. Запустить файл проекта AddInNative_SynchClientServer.sln в каталоге AddInNative_SynchClientServerWindows.
  4. Скачать набор библиотек Boost. Проверенная версия 1.72.0.
  5. Запустить bootstrap.bat для сборки утилиты b2.exe.
  6. Собрать файлы .lib библиотек Boost с помощью утилиты b2.exe для платформ win32 и x64. Файлы для разных платформ помещаются в разные папки. Указать пути к каталогам библиотек в свойствах проекта.
    
    b2.exe -j4 toolset=msvc-14.2 address-model=64 architecture=x86 link=static threading=multi runtime-link=static --build-type=complete --build-dir=build\x64 stage

b2.exe -j4 toolset=msvc-14.2 address-model=32 architecture=x86 link=static threading=multi runtime-link=static --build-type=complete --build-dir=build\x32 stage

7. Скачать библиотеку [rapidjson](https://github.com/Tencent/rapidjson/) для работы с JSON. Указать пути к каталогам 
библиотеки в свойствах проекта.

## Сборка компоненты под Linux
Сборку необходимо производить только на Ubuntu 18 или Debian 9.
1. Скачать исходники репозитория данного проекта. 
2. Скачать набор библиотек Boost. Проверенная версия 1.72.0.
```sh
sudo apt-get update
sudo apt install build-essential cmake

cd
wget -O boost_1_72_0.tar.gz https://sourceforge.net/projects/boost/files/boost/1.72.0/boost_1_72_0.tar.gz/download --no-check-certificate
tar xzvf boost_1_72_0.tar.gz
cd boost_1_72_0/

sudo apt-get update
sudo apt-get install build-essential g++ python-dev autotools-dev libicu-dev libbz2-dev libboost-all-dev

./bootstrap.sh
./b2
sudo ./b2 install
  1. Скачать и установить библиотеку rapidjson.
    git clone https://github.com/Tencent/rapidjson.git
    cd rapidjson/
    cmake .
    make install
  2. Собрать динамическую библиотеку
    cd linux
    mkdir tmp_build && cd tmp_build
    cmake -DCMAKE_BUILD_TYPE=Release ..
    cmake --build .

Планы доработки компоненты

  1. Реализовать функционал работы с портам UDP и COM.
  2. Реализовать возможность использования символов окончания сообщения, для того чтобы прекращать чтение данных из буфера сокета после того как такой символ был прочитан (сейчас применяется параметр delayReadingFromSocket).
  3. Реализовать возможность использования сырых сокетов.

Фичи ждут своих контрибьютеров.

Как принять участие в разработке

Если Вы нашли какую-нибудь ошибку или хотите предложить доработку проекта, то порядок следующий:

  1. Возьмите в работу какой-то issue или заведите новый issue.
  2. Сделайте fork репозитория через кнопку в интерфейсе репозитория github-а.
  3. Склонируйте репозиторий себе на машину.
  4. На основании ветки master создайте новую ветку с номером задачи, например:
    git checkout -b issue-9999
  5. Выполните необходимые доработки и зафиксируйте необходимые изменения в исходниках в git.
  6. Зайдите в репозиторий на github в векту мастер и создайте pull реквест через кнопку New pull request на вкладке pull requests.
  7. После проверки pull реквеста, он либо будет отправлен Вам на доработку, или принят в main ветку.

Используемые сторонние продукты

  1. Boost, в частности Boost.Asio - работа с TCP сокетами.
  2. Rapidjson - работа с форматом JSON.