Предоставляет доступ к обработчикам WebSocket-клиента.
Полное описание, параметры и ограничения
Модуль WebSocket клиента (WebSocket client module)
Модуль WebSocket клиента (WebSocket client module)
Доступен, начиная с версии 8.3.27.
События:
ПередПодключением (BeforeConnect)
ПриЗакрытииСоединения (OnCloseConnection)
ПриОткрытииСоединения (OnOpenConnection)
ПриОшибке (OnError)
ПриПолученииСообщения (OnMessage)
Описание:
Предоставляет доступ к обработчикам WebSocket-клиента.
Доступность:
Сервер, толстый клиент, внешнее соединение.
Использование в версии:
Доступен, начиная с версии 8.3.27.
Методическая информация
Практические примеры
Пять событий модуля WebSocket-клиента: от подготовки параметров до настоящего echo и закрытия
Настоящий модуль собственного метаобъекта Эхо85 обрабатывает клиент, соединение, строковые/двоичные сообщения и статус закрытия. Серверное владение определяется паспортом параметров, не недоступным серверу свойством Ключ соединения.
Где и как запускать
Полный МОДУЛЬ метаобъекта WebSocket-клиент Эхо85 конфигурации УчебныйСохраненныйWebSocket85. Серверные сеансы собственного пользователя; не модуль формы. В1С не выполнялся.
Подготовка
НОВАЯ отдельная пустая файловая база точно8.5.1.1343. Конфигурация УчебныйСохраненныйWebSocket85, Русский, совместимость Не использовать. Нет прикладных объектов, регистров, расширений, подписок и заданий; модули конфигурации вручную пустые. Не использовать рабочую базу или базу прямого WebSocket-урока.
Общие → WebSocket-клиенты: единственный Эхо85, URLСервера=ws://127.0.0.1:18485/own-ws85, Предопределенный=Нет, ПодключатьАвтоматически=Нет, ИмяПользователяИнформационнойБазы=УчебныйWS85. В МОДУЛЬ этого метаобъекта вставить полный блок событий. Это не модуль формы или общего модуля; имена пяти процедур соответствуют нативным событиям и вручную к кнопкам не привязываются.
Общие → Общие модули → УчебныйСохраненныйWS85: Сервер=Да, Вызов сервера=Да, Клиент=Нет, Глобальный=Нет, Привилегированный=Нет. Вставить весь контроллер, включая проверку пустого стенда, ключа, реального клиента, настройки и записи журнала. Все вызываемые экспортные процедуры присутствуют в этом блоке.
Создайте ТОЛЬКО в своей пустой базе собственную роль УчебнаяАдминистрацияWS85. Разрешите использование своей конфигурации/клиента, открытие своей внешней обработки и обязательно право Администрирование данных: установленная справка требует его для Записать и Удалить. Создайте пользователя УчебныйWS85 с этой ролью. Проверить его права вручную; серверные процедуры и сеансы событий должны работать именно под ним, не под администратором рабочей базы.
Свою внешнюю обработку ПультСохраненногоWS85 создать с управляемой основной формой. Реквизиты Согласие — Булево; КлючСтенда и КлючВосстановления — Строка100; Результат — Строка неограниченной длины. Флажок согласия редактируемый, КлючСтенда только просмотр для копирования в фильтр журнала, КлючВосстановления редактируемый только для настоящего ранее записанного своего ключа, Результат многострочный только просмотра.
Создать шесть команд и кнопок Подготовить85, Подключить85, Состояние85, Отключить85, Удалить85, ВосстановитьКлюч85 с одноименными действиями. Основной блок формы вставить полностью, назначить ПриОткрытии. Команды вызывают сервер, клиенту возвращаются только строки, не клиент/соединение/параметры.
Будущая ручная подготовка: сохранить свою обработку, сохранить и обновить ТОЛЬКО собственную конфигурацию F7. Включить в своем журнале регистрации Информация и Ошибка; открыть свой журнал и фильтровать Событие=OwnWS85.*, Метаданные=WebSocketКлиент.Эхо85, Данные=точный собственный ключ. Без включенного уровня и фактических записей событий нельзя объявлять echo подтвержденным.
Дополнительный own-ws85.cjs — JavaScript, НЕ модуль1С. Это тот же полный loopback-сервер из прямого урока. Только после отдельного разрешения в новой собственной папке вручную npm init -y, npm install ws --ignore-scripts --save-exact, npm ls ws и node own-ws85.cjs. Порт127.0.0.1:18485 предварительно вручную проверить как свободный и принадлежащий только своему стенду. Здесь ни сервер, ни установка, ни сеть не запускались.
Будущий ручной опыт: отметить согласие, Подготовить85 и сохранить настоящий показанный UUID-ключ. Клиент сохраняется, но автоматически не подключается. Подключить85 — отдельная команда. Фактические события Открытие, ТекстEcho и ДвоичныйEcho читать в журнале своего ключа, не считать подтверждением строку о запросе подключения.
Будущее завершение: Отключить85, отдельным Состояние85 подтвердить реальное Закрыто и выключенное автоподключение, затем Удалить85. Журнал не удаляется. Сохраненный клиент не исчезает от закрытия формы. Если форма была закрыта до завершения, открыть новую, перенести СОХРАНЕННЫЙ настоящий ключ в КлючВосстановления и нажать ВосстановитьКлюч85: сервер заново проверяет существующего своего клиента до присваивания ключа пульту.
runtimeVerifiedScenarios=0. Записи клиентских данных/журнала, фоновые сеансы, права, Basic-handshake, echo и отключение в1С здесь не выполнялись. Эти разрешенные будущие изменения касаются только своей новой базы. Производственные конфигурации/данные не читались и не изменялись.
Процедура ПередПодключением(Клиент)
Ключ = Клиент.Ключ;
УчебныйСохраненныйWS85.СверитьСвоегоКлиента(Клиент, Ключ);
УчебныйСохраненныйWS85.НастроитьСвоиПараметры(Клиент, Ключ);
УчебныйСохраненныйWS85.ЗаписатьСвоеСобытие(Ключ, "ПередПодключением", "Настроены настоящие Клиент.ПараметрыСоединения перед попыткой соединения");
КонецПроцедуры
Процедура ПриОткрытииСоединения(Соединение)
Ключ = УчебныйСохраненныйWS85.ПолучитьКлючСвоегоСобытия(Соединение);
УчебныйСохраненныйWS85.ЗаписатьСвоеСобытие(Ключ, "Открытие", "Реальное событие открытия; получение echo еще не подтверждено");
Попытка
Соединение.ОтправитьСообщение("own-ws85-persist-text:" + Ключ);
Исключение
ВызватьИсключение УчебныйСохраненныйWS85.ЗавершитьСвоеСоединениеПослеОшибки(Соединение, Ключ, ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры
Процедура ПриПолученииСообщения(Соединение, Сообщение)
Ключ = УчебныйСохраненныйWS85.ПолучитьКлючСвоегоСобытия(Соединение);
Попытка
Если ТипЗнч(Сообщение) = Тип("Строка") Тогда
Если Сообщение <> "own-ws85-persist-text:" + Ключ Тогда
ВызватьИсключение "Не совпал точный текстовый echo своего сохраненного клиента";
КонецЕсли;
УчебныйСохраненныйWS85.ЗаписатьСвоеСобытие(Ключ, "ТекстEcho", "Подтвержден точный собственный текст");
Данные = Base64Значение("UElORw==");
Соединение.ОтправитьСообщение(Данные);
ИначеЕсли ТипЗнч(Сообщение) = Тип("ДвоичныеДанные") Тогда
Если Base64Строка(Сообщение) <> "UElORw==" Тогда ВызватьИсключение "Не совпали свои четыре байта PING"; КонецЕсли;
УчебныйСохраненныйWS85.ЗаписатьСвоеСобытие(Ключ, "ДвоичныйEcho", "Подтверждены свои четыре байта PING");
Иначе
ВызватьИсключение "Недокументированный тип входящего сообщения";
КонецЕсли;
Исключение
ВызватьИсключение УчебныйСохраненныйWS85.ЗавершитьСвоеСоединениеПослеОшибки(Соединение, Ключ, ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры
Процедура ПриОшибке(Соединение, КодОшибки, Описание)
Ключ = УчебныйСохраненныйWS85.ПолучитьКлючСвоегоСобытия(Соединение);
Если ТипЗнч(КодОшибки) <> Тип("Строка") Или ТипЗнч(Описание) <> Тип("Строка") Тогда
ВызватьИсключение "Не совпали два строковых параметра ошибки";
КонецЕсли;
ВызватьИсключение УчебныйСохраненныйWS85.ЗавершитьСвоеСоединениеПослеОшибки(Соединение, Ключ,
Лев(КодОшибки, 100) + ": " + Лев(Описание, 1000));
КонецПроцедуры
Процедура ПриЗакрытииСоединения(Соединение, КодЗакрытия)
Ключ = УчебныйСохраненныйWS85.ПолучитьКлючСвоегоСобытия(Соединение);
Если ТипЗнч(КодЗакрытия) <> Тип("Число") Тогда ВызватьИсключение "Нужен реальный числовой код закрытия"; КонецЕсли;
УчебныйСохраненныйWS85.ЗаписатьСвоеСобытие(Ключ, "Закрытие", "Реальный статус " + Строка(КодЗакрытия) + ";1005 означает отсутствие полученного кода");
КонецПроцедурыУчебныйСохраненныйWS85
УчебныйСохраненныйWebSocket85 → Общие модули → УчебныйСохраненныйWS85 → Модуль
Полный серверный непривилегированный контроллер своих клиентских данных и собственного регистрационного журнала.
Функция ПроверитьСвойКлюч(Ключ) Экспорт
Если ТипЗнч(Ключ) <> Тип("Строка") Или Лев(Ключ, 17) <> "own-ws85-persist-" Тогда
ВызватьИсключение "Нужен только свой ключ сохраненного WebSocket-опыта";
КонецЕсли;
Хвост = Сред(Ключ, 18);
UUID = Новый УникальныйИдентификатор(Хвост);
Если Строка(UUID) <> Хвост Тогда
ВызватьИсключение "Нужен точный собственный UUID, не произвольное имя";
КонецЕсли;
Возврат Ключ;
КонецФункции
Функция ПолучитьСвоиМетаданные() Экспорт
ПроверитьСвойПустойСтенд();
Если ИмяПользователя() <> "УчебныйWS85" Или Метаданные.WebSocketКлиенты.Количество() <> 1 Тогда
ВызватьИсключение "Нужен свой пользователь и единственный собственный метаобъект";
КонецЕсли;
Описание = Метаданные.WebSocketКлиенты.Найти("Эхо85");
Если Описание = Неопределено Или Описание.Предопределенный Или Описание.ПодключатьАвтоматически
Или Описание.URLСервера <> "ws://127.0.0.1:18485/own-ws85" Тогда
ВызватьИсключение "Нужно свое непредопределенное Эхо85 с ручным подключением к loopback";
КонецЕсли;
Возврат Описание;
КонецФункции
Процедура СверитьСвоегоКлиента(Клиент, Ключ) Экспорт
ПроверитьСвойКлюч(Ключ);
Описание = ПолучитьСвоиМетаданные();
Если ТипЗнч(Клиент) <> Тип("WebSocketКлиент") Или Клиент.Предопределенный Или Клиент.Ключ <> Ключ
Или Клиент.Метаданные <> Описание Или Клиент.URLСервера <> "ws://127.0.0.1:18485/own-ws85"
Или Клиент.ИмяПользователяИнформационнойБазы <> "УчебныйWS85" Или Клиент.ПодключатьАвтоматически Тогда
ВызватьИсключение "Не совпал паспорт своего клиента; существующий чужой объект не меняется";
КонецЕсли;
КонецПроцедуры
Функция ПолучитьСвоегоКлиента(Ключ) Экспорт
ПроверитьСвойКлюч(Ключ);
Клиент = WebSocketКлиенты.НайтиПоКлючу(Ключ);
Если Клиент = Неопределено Тогда
ВызватьИсключение "Свой сохраненный клиент не найден";
КонецЕсли;
СверитьСвоегоКлиента(Клиент, Ключ);
Возврат Клиент;
КонецФункции
Процедура НастроитьСвоиПараметры(Клиент, Ключ) Экспорт
СверитьСвоегоКлиента(Клиент, Ключ);
Параметры = Клиент.ПараметрыСоединения;
Если ТипЗнч(Параметры) <> Тип("ПараметрыWebSocketКлиентСоединения") Тогда
ВызватьИсключение "Нужен настоящий readonly-источник изменяемых сетевых параметров";
КонецЕсли;
Параметры.ИспользоватьАутентификациюОС = Ложь;
Параметры.ИспользоватьПроксиОС = Ложь;
Параметры.Пользователь = "own-ws85";
Параметры.Пароль = "own-local-only";
Параметры.Таймаут = 10;
Заголовки = Новый Соответствие;
Заголовки.Вставить("X-Own-Lab", "owned-ws85-direct");
Параметры.Заголовки = Заголовки;
Параметры.ДополнительныеПараметры = Новый Структура("Метка,Ключ", "owned-ws85-persistent", Ключ);
КонецПроцедуры
Процедура ЗаписатьСвоеСобытие(Ключ, Имя, Комментарий, Ошибка = Ложь) Экспорт
ПроверитьСвойКлюч(Ключ);
Описание = ПолучитьСвоиМетаданные();
Разрешенные = СтрРазделить("ПередПодключением,Открытие,ТекстEcho,ДвоичныйEcho,Ошибка,Закрытие,Подготовлен,ПодключениеЗапрошено,ОтключениеЗапрошено,УдалениеЗапрошено", ",");
Если Разрешенные.Найти(Имя) = Неопределено Тогда
ВызватьИсключение "Событие не принадлежит собственному учебному протоколу";
КонецЕсли;
БезПароля = СтрЗаменить(Комментарий, "own-local-only", "[учебный пароль скрыт]");
БезПароля = СтрЗаменить(БезПароля, "b3duLXdzODU6b3duLWxvY2FsLW9ubHk=", "[учебный Basic скрыт]");
Уровень = ?(Ошибка, УровеньЖурналаРегистрации.Ошибка, УровеньЖурналаРегистрации.Информация);
ЗаписьЖурналаРегистрации("OwnWS85." + Имя, Уровень, Описание, Ключ, Лев(БезПароля, 1200));
КонецПроцедуры
Функция ПолучитьКлючСвоегоСобытия(Соединение) Экспорт
ПолучитьСвоиМетаданные();
Если ТипЗнч(Соединение) <> Тип("WebSocketКлиентСоединение") Или Соединение.URLСервера <> "ws://127.0.0.1:18485/own-ws85"
Или Соединение.ИмяПользователяИнформационнойБазы <> "УчебныйWS85" Тогда
ВызватьИсключение "Событие не принадлежит своему локальному соединению";
КонецЕсли;
Параметры = Соединение.Параметры;
Дополнительные = Параметры.ДополнительныеПараметры;
Если ТипЗнч(Дополнительные) <> Тип("Структура") Или Дополнительные.Метка <> "owned-ws85-persistent" Тогда
ВызватьИсключение "Нет собственного паспорта события";
КонецЕсли;
Возврат ПроверитьСвойКлюч(Дополнительные.Ключ);
КонецФункции
Функция ЗавершитьСвоеСоединениеПослеОшибки(Соединение, Ключ, ТекстОшибки) Экспорт
Если ПолучитьКлючСвоегоСобытия(Соединение) <> Ключ Тогда
ВызватьИсключение "Чужое или неподтвержденное соединение не закрывается";
КонецЕсли;
ИтогОшибки = ТекстОшибки;
Попытка
ЗаписатьСвоеСобытие(Ключ, "Ошибка", ТекстОшибки, Истина);
Исключение
ИтогОшибки = ИтогОшибки + " Журнал НЕ записан: " + ОписаниеОшибки();
КонецПопытки;
Попытка
Соединение.Закрыть();
Исключение
ИтогОшибки = ИтогОшибки + " Свое закрытие НЕ подтверждено: " + ОписаниеОшибки();
КонецПопытки;
Возврат ИтогОшибки;
КонецФункции
Функция ПодготовитьСвоегоКлиента(Ключ, Согласие) Экспорт
Если Не Согласие Тогда ВызватьИсключение "Нужно согласие на запись только своего нового клиента"; КонецЕсли;
ПроверитьСвойКлюч(Ключ);
Описание = ПолучитьСвоиМетаданные();
Если WebSocketКлиенты.НайтиПоКлючу(Ключ) <> Неопределено Тогда
ВызватьИсключение "Ключ уже занят; существующий клиент не перезаписывается";
КонецЕсли;
Клиент = WebSocketКлиенты.СоздатьКлиента(Описание, Ключ);
Клиент.URLСервера = "ws://127.0.0.1:18485/own-ws85";
Клиент.ИмяПользователяИнформационнойБазы = "УчебныйWS85";
Клиент.ПодключатьАвтоматически = Ложь;
СверитьСвоегоКлиента(Клиент, Ключ);
Попытка
НастроитьСвоиПараметры(Клиент, Ключ);
Клиент.Записать();
Повторно = ПолучитьСвоегоКлиента(Ключ);
Если Повторно.ПолучитьТекущееСоединение() <> Неопределено Тогда
ВызватьИсключение "Подготовка не должна автоматически открывать соединение";
КонецЕсли;
ЗаписатьСвоеСобытие(Ключ, "Подготовлен", "Свой клиент записан, автоподключение выключено");
Исключение
ОшибкаПодготовки = ОписаниеОшибки();
Попытка
СверитьСвоегоКлиента(Клиент, Ключ);
Если Клиент.ПолучитьТекущееСоединение() <> Неопределено Тогда Клиент.Отключить(); КонецЕсли;
Клиент.Удалить();
Исключение
ОшибкаПодготовки = ОшибкаПодготовки + " Удаление своего нового клиента после ошибки НЕ подтверждено: " + ОписаниеОшибки();
КонецПопытки;
ВызватьИсключение ОшибкаПодготовки;
КонецПопытки;
Возврат "Свой клиент записан без подключения. Сохраните ключ для журнала и завершения: " + Ключ;
КонецФункции
Функция ПодключитьСвоегоКлиента(Ключ, Согласие) Экспорт
Если Не Согласие Тогда ВызватьИсключение "Нужно согласие на свое локальное соединение"; КонецЕсли;
Клиент = ПолучитьСвоегоКлиента(Ключ);
Если Клиент.ПолучитьТекущееСоединение() <> Неопределено Тогда
ВызватьИсключение "Текущее собственное соединение уже предоставлено; повторное подключение не начинается";
КонецЕсли;
Попытка
Соединение = Клиент.Подключить();
Если Соединение <> Неопределено И ТипЗнч(Соединение) <> Тип("WebSocketКлиентСоединение") Тогда
ВызватьИсключение "Не совпал тип результата Подключить";
КонецЕсли;
ЗаписатьСвоеСобытие(Ключ, "ПодключениеЗапрошено", "Подключить выполнен; результат типа " + Строка(ТипЗнч(Соединение)));
Исключение
ОшибкаПодключения = ОписаниеОшибки();
Попытка
Клиент.Отключить();
Исключение
ОшибкаПодключения = ОшибкаПодключения + " Собственное отключение после ошибки НЕ подтверждено: " + ОписаниеОшибки();
КонецПопытки;
ВызватьИсключение ОшибкаПодключения;
КонецПопытки;
Возврат "Подключение запрошено. Echo подтверждают отдельные события OwnWS85.ТекстEcho и OwnWS85.ДвоичныйEcho своего ключа";
КонецФункции
Функция ПрочитатьСвойСтатус(Ключ) Экспорт
Клиент = ПолучитьСвоегоКлиента(Ключ);
Возврат "Фактическое состояние своего клиента: " + Строка(Клиент.ПолучитьСостояниеСоединения())
+ "; автоподключение: " + Строка(Клиент.ПодключатьАвтоматически)
+ ". Этот статус не доказывает получение сообщений; откройте журнал OwnWS85 по своему ключу";
КонецФункции
Функция ОтключитьСвоегоКлиента(Ключ) Экспорт
Клиент = ПолучитьСвоегоКлиента(Ключ);
Клиент.Отключить();
Если Клиент.ПодключатьАвтоматически Тогда
ВызватьИсключение "Отключить не выключил собственное автоподключение";
КонецЕсли;
ЗаписатьСвоеСобытие(Ключ, "ОтключениеЗапрошено", "Прежнее соединение после Отключить больше не используется");
Возврат ПрочитатьСвойСтатус(Ключ) + ". Ранее полученный объект соединения НЕ использовать";
КонецФункции
Функция УдалитьСвоегоКлиента(Ключ, Согласие) Экспорт
Если Не Согласие Тогда ВызватьИсключение "Нужно согласие на удаление только своего сохраненного клиента"; КонецЕсли;
Клиент = ПолучитьСвоегоКлиента(Ключ);
Если Клиент.ПолучитьСостояниеСоединения() <> СостояниеWebSocketСоединения.Закрыто Тогда
ВызватьИсключение "Сначала отключите и отдельно подтвердите фактическое Закрыто";
КонецЕсли;
ЗаписатьСвоеСобытие(Ключ, "УдалениеЗапрошено", "Удаляется только проверенный собственный непредопределенный клиент");
Клиент.Удалить();
После = WebSocketКлиенты.НайтиПоКлючу(Ключ);
Если После = Неопределено Тогда
Возврат "Удалить выполнен; менеджер не нашел клиента своего ключа. Журнал событий не удалялся";
КонецЕсли;
Возврат "Удалить выполнен; менеджер еще предоставил объект типа " + Строка(ТипЗнч(После))
+ ". Не считать это подтверждением исчезновения объекта; исследуйте только свой стенд. Журнал не удалялся";
КонецФункции
Процедура ПроверитьСвойПустойСтенд()
Если Метаданные.Справочники.Количество() <> 0 Или Метаданные.Документы.Количество() <> 0 Или Метаданные.Перечисления.Количество() <> 0 Или Метаданные.РегистрыСведений.Количество() <> 0 Или Метаданные.РегистрыНакопления.Количество() <> 0 Или Метаданные.РегистрыБухгалтерии.Количество() <> 0 Или Метаданные.РегистрыРасчета.Количество() <> 0 Или Метаданные.ПланыВидовХарактеристик.Количество() <> 0 Или Метаданные.ПланыВидовРасчета.Количество() <> 0 Или Метаданные.ПланыСчетов.Количество() <> 0 Или Метаданные.ПланыОбмена.Количество() <> 0 Или Метаданные.Последовательности.Количество() <> 0 Или Метаданные.БизнесПроцессы.Количество() <> 0 Или Метаданные.Задачи.Количество() <> 0 Или Метаданные.ОбщиеРеквизиты.Количество() <> 0 Или Метаданные.ПодпискиНаСобытия.Количество() <> 0 Или Метаданные.РегламентныеЗадания.Количество() <> 0 Или Метаданные.ВнешниеИсточникиДанных.Количество() <> 0 Тогда
ВызватьИсключение "Нужен только свой новый пустой учебный стенд";
КонецЕсли;
Если Метаданные.ЕстьИзмененияРасширениямиКонфигурации() Тогда
ВызватьИсключение "Свой сеанс не должен изменяться расширениями";
КонецЕсли;
Источники = Новый Массив;
Источники.Добавить(ИсточникРасширенийКонфигурации.БазаДанных);
Источники.Добавить(ИсточникРасширенийКонфигурации.СеансАктивные);
Источники.Добавить(ИсточникРасширенийКонфигурации.СеансОтключенные);
Для Каждого Источник Из Источники Цикл
Расширения = РасширенияКонфигурации.Получить(, Источник);
Если ТипЗнч(Расширения) <> Тип("Массив") Тогда
ВызватьИсключение "Native список расширений должен иметь тип Массив";
КонецЕсли;
Если Расширения.Количество() <> 0 Тогда
ВызватьИсключение "В своём новом стенде нет расширений базы или сеанса";
КонецЕсли;
КонецЦикла;
Если ТранзакцияАктивна() Тогда
ВызватьИсключение "Чужая или вложенная активная транзакция не допускается";
КонецЕсли;
КонецПроцедурыПультСохраненногоWS85
Своя внешняя обработка ПультСохраненногоWS85 → Управляемая основная форма → Модуль
Полный клиент-серверный пульт своих шести команд; клиенту возвращаются строки. Настоящий UUID сохранен для журнала и восстановления.
&НаКлиенте
Процедура ПриОткрытии(Отказ)
Согласие = Ложь;
КлючСтенда = "";
КлючВосстановления = "";
Результат = "Подготовьте свой сервер и права. Сохраненный клиент еще не создан";
КонецПроцедуры
&НаКлиенте
Процедура Подготовить85(Команда)
Если Не Согласие Или КлючСтенда <> "" Тогда
ВызватьИсключение "Нужны согласие и новый опыт; старый ключ не заменяется";
КонецЕсли;
КлючСтенда = "own-ws85-persist-" + Строка(Новый УникальныйИдентификатор);
Результат = ПодготовитьНаСервере(КлючСтенда, Согласие);
КонецПроцедуры
&НаСервереБезКонтекста
Функция ПодготовитьНаСервере(Ключ, Согласие)
Возврат УчебныйСохраненныйWS85.ПодготовитьСвоегоКлиента(Ключ, Согласие);
КонецФункции
&НаКлиенте
Процедура Подключить85(Команда)
Результат = ПодключитьНаСервере(КлючСтенда, Согласие);
КонецПроцедуры
&НаСервереБезКонтекста
Функция ПодключитьНаСервере(Ключ, Согласие)
Возврат УчебныйСохраненныйWS85.ПодключитьСвоегоКлиента(Ключ, Согласие);
КонецФункции
&НаКлиенте
Процедура Состояние85(Команда)
Результат = СостояниеНаСервере(КлючСтенда);
КонецПроцедуры
&НаСервереБезКонтекста
Функция СостояниеНаСервере(Ключ)
Возврат УчебныйСохраненныйWS85.ПрочитатьСвойСтатус(Ключ);
КонецФункции
&НаКлиенте
Процедура Отключить85(Команда)
Результат = ОтключитьНаСервере(КлючСтенда);
КонецПроцедуры
&НаСервереБезКонтекста
Функция ОтключитьНаСервере(Ключ)
Возврат УчебныйСохраненныйWS85.ОтключитьСвоегоКлиента(Ключ);
КонецФункции
&НаКлиенте
Процедура Удалить85(Команда)
Результат = УдалитьНаСервере(КлючСтенда, Согласие);
КонецПроцедуры
&НаСервереБезКонтекста
Функция УдалитьНаСервере(Ключ, Согласие)
Возврат УчебныйСохраненныйWS85.УдалитьСвоегоКлиента(Ключ, Согласие);
КонецФункции
&НаКлиенте
Процедура ВосстановитьКлюч85(Команда)
Если Не Согласие Или КлючСтенда <> "" Тогда
ВызватьИсключение "Для восстановления нужны согласие и пустой текущий ключ; другой опыт не подменяется";
КонецЕсли;
Проверка = СостояниеНаСервере(КлючВосстановления);
КлючСтенда = КлючВосстановления;
Результат = "Восстановлен только реально найденный собственный клиент. " + Проверка;
КонецПроцедурыown-ws85.cjs — JavaScript echo-сервер, НЕ модуль1С
Новая отдельная папка собственного опыта → own-ws85.cjs
Полный ранее статически проверенный loopback-сервер Node.js/ws; не запускался на реальной сети.
const http=require('node:http');
const {WebSocketServer,WebSocket}=require('ws');
const host='127.0.0.1';
const port=18485;
const endpoint='/own-ws85';
const credentials='Basic '+Buffer.from('own-ws85:own-local-only','ascii').toString('base64');
const server=http.createServer((request,response)=>{response.writeHead(404);response.end('own-ws85 only');});
const sockets=new Set();
const wss=new WebSocketServer({noServer:true,maxPayload:4096,perMessageDeflate:false});
server.maxConnections=8;
server.headersTimeout=10000;
server.requestTimeout=10000;
function reportError(error) {console.error('own-ws85 failure:',error.code||error.message);}
function rejectUpgrade(socket,status,challenge='') {
socket.end('HTTP/1.1 '+status+'\r\nConnection: close\r\nContent-Length: 0\r\n'+challenge+'\r\n');
}
server.on('connection',socket=>{
sockets.add(socket);
socket.setTimeout(60000,()=>socket.destroy());
socket.on('error',reportError);
socket.on('close',()=>sockets.delete(socket));
});
server.on('upgrade',(request,socket,head)=>{
if(request.socket.remoteAddress!==host||request.method!=='GET'||request.url!==endpoint||request.headers.host!==host+':'+port||request.headers['x-own-lab']!=='owned-ws85-direct') {
rejectUpgrade(socket,'403 Forbidden');return;
}
if(request.headers.authorization!==credentials) {
rejectUpgrade(socket,'401 Unauthorized','WWW-Authenticate: Basic realm="own-ws85"\r\n');return;
}
if(wss.clients.size>=4) {rejectUpgrade(socket,'503 Service Unavailable');return;}
try {wss.handleUpgrade(request,socket,head,ws=>wss.emit('connection',ws,request));}
catch(error) {reportError(error);socket.destroy();}
});
wss.on('error',reportError);
wss.on('connection',ws=>{
let received=0;
ws.on('error',reportError);
ws.on('message',(data,isBinary)=>{
received++;
if(received>8||data.length>512||ws.bufferedAmount>4096) {ws.close(1008,'own-lab limit');return;}
if(ws.readyState!==WebSocket.OPEN) return;
ws.send(data,{binary:isBinary},error=>{if(error){reportError(error);ws.terminate();}});
});
ws.on('close',code=>console.log('own-ws85 connection closed:',code));
});
server.on('error',error=>{reportError(error);process.exitCode=1;});
server.listen(port,host,()=>console.log('own-ws85 listening on ws://'+host+':'+port+endpoint));
process.once('SIGINT',()=>{
for(const client of wss.clients) client.terminate();
for(const socket of sockets) socket.destroy();
wss.close();
server.close(error=>{if(error){reportError(error);process.exitCode=1;}});
});Разбор по шагам
Новый UUID создается пультом ДО серверного вызова и остается у пользователя даже при ошибке подготовки. Сервер проверяет канонический ключ с own-ws85-persist-префиксом, собственные имя базы/версию/пользователя и единственный непредопределенный Эхо85. Занятый ключ отклоняется; существующий клиент не перезаписывается.
СоздатьКлиента получает настоящий метаобъект и ключ. URL, ИмяПользователяИнформационнойБазы и выключенное ПодключатьАвтоматически задаются у своего нового клиента. Записать — процедура сохранения, не подключение и не возвращаемое значение. Клиент повторно находится и проверяется; после неуспешной подготовки независимо предпринимается удаление только этого собственного нового клиента.
Подключить вызывается отдельно только без текущего соединения. Его результат допускает Неопределено или настоящий WebSocketКлиентСоединение. Ни сам результат, ни строка запроса не выдаются за завершенный handshake/echo. При ошибке соединения вызывается только собственный Отключить, его ошибка сохраняется.
ПередПодключением получает настоящий Клиент и заново задает Клиент.ПараметрыСоединения: публичную Basic-пару, заголовок, таймаут и паспорт событий. Ошибка исходного нативного фрагмента Клиент.Параметры не копируется. У readonly-источника не заменяется объект, меняются свойства реально полученных параметров.
Модуль метаданных содержит пять точных событий. Открытие отправляет свой UUID-текст, подтвержденное текстовое echo отправляет четыре байта PING, двоичный callback проверяет Base64. Ошибка получает две строки, закрытие — числовой код;1005 означает отсутствие кода. Тексты ошибок/закрытия фактические, а не заранее придуманное сообщение.
Серверные callback НЕ используют Соединение.Ключ, доступный только толстому клиенту. Ключ берется из реальных Параметры.ДополнительныеПараметры, заполненных перед подключением; сверяются marker/URL/пользователь и собственный UUID. Клиент.Ключ в ПередПодключением — другой API, допускающий сервер.
Регистрационный журнал связывает событие с настоящим метаобъектом Эхо85, строкой собственного ключа в Данные и ограниченным комментарием. Разрешенные имена проверяются точно; пароль/Basic-текст скрываются. Журнал регистрации не равен сообщению формы: его нужно открыть и реально проверить собственный фильтр и включенные уровни.
Отключить выключает автоподключение и делает прежнее соединение непригодным. Контроллер больше не обращается к прежнему объекту: статус читается заново через клиента. Удалить разрешен только непредопределенному собственному клиенту в фактическом Закрыто; исходные метаданные и регистрационный журнал остаются.
Удалить сбрасывает данные к значениям метаданных. После него диагностируется реальный результат НайтиПоКлючу: если менеджер еще предоставил объект, это явно не объявляется подтверждением его исчезновения. Восстановление пульта допускает только действительно найденный сохраненный клиент своего стенда; новый UUID вместо потерянного ключа не восстанавливает старый опыт.
Как проверить результат
Будущий положительный контроль: Подготовлен без автоматического открытия; после отдельного Подключить фактические ПередПодключением/Открытие/ТекстEcho/ДвоичныйEcho в собственном журнале, затем отдельные отключение, Закрыто и удаление только своего клиента.
Будущий отрицательный контроль: отсутствие Согласие, занятый или иной key-префикс, другой пользователь/метаданные/URL, предопределенность либо автоподключение должны прекращать изменения. Отдельно проверить отказ записи/удаления без права Администрирование данных; подготовка не должна выдавать успешный статус.
Будущий контроль ошибок: остановленный сервер или неверные публичные Basic-данные не дают события ТекстEcho/ДвоичныйEcho. Ошибка/неподтвержденное завершение отражается реально. Отключенный объект соединения не используется для повторной отправки или чтения.
Будущий контроль завершения и восстановления: если пульт закрыт до удаления, восстановить его только сохраненным настоящим ключом; неизменные владение/URL/пользователь снова проверены. Журнал не очищается. При отсутствии фактической записи Закрытие не придумывать ее код: исследовать callback-жизненный цикл своего клиента.
runtimeVerifiedScenarios=0: текущие проверки — source/native/JSON/поиск/копирование, не выполнение этих шагов в1С. Каждый факт обмена и записи журнала требует отдельного разрешенного ручного протокола.
Ожидаемый результат
Полный собственный модуль пяти событий с корректными параметрами, точным payload, отдельной обработкой ошибок и фактическим регистрационным журналом своего key.
На что обратить внимание
Данные сохраненного клиента и регистрационный журнал — реальные будущие записи собственной учебной базы. Не запускать этот код в production. Право Администрирование данных не обходится привилегированным модулем; оно должно быть явно разрешено только своей учебной роли.
ИмяПользователяИнформационнойБазы — пользователь сеанса обработчиков; Пользователь/Пароль параметров — HTTP-учебная пара. Публичный пароль не является защитой рабочего сервиса. Для production нужны отдельные TLS/wss, секреты и политика доступа.
Подключить может вернуть Неопределено. Нельзя угадать успешный handshake из результата вызова. Статус Открыто не доказывает точный echo; нужны фактические события по собственному UUID.
После Отключить прежнее соединение использовать нельзя. Событие закрытия дает платформенный объект только в контексте своего callback; наличие/доступность его паспорта и фактической записи надо проверить вручную, не обращаться к сохраненной старой ссылке из пульта.
События разных сеансов могут попасть в один момент журнала. Порядок строк/секунд не доказывает порядок исполнения; проверяйте собственный key и смысл событий. Не требуйте статуса1000, фиксированного описания сетевой ошибки или обязательного полного журнала при выключенных уровнях.
Предопределенный клиент удалять нельзя. Контроллер проверяет Предопределенный и всю собственную пару key/метаданные/URL/пользователь до удаления, не перебирает и не удаляет массив чужих клиентов.
Сохраненный клиент живет независимо от формы. Закрытие пульта не является завершением или удалением. Потеря ключа требует исследования только своей базы; новая метка не относится к старому клиенту. Код восстановления не выполняет запись или подключение.
JS-сервер проверяется отдельно как JavaScript, не BSL. Его библиотека/сеть/1С не запускались. Переиспользование полного ранее проверенного сервера не является новым runtime-тестом.
Пример не запускался в вашей базе. Перед применением проверьте его в указанном учебном контексте.