Поинтегрируем: WebSocket’ы в платформе 1С. Часть 1

14.01.25

Разработка - Механизмы платформы 1С

В платформе 8.3.27 появилась возможность использовать WebSocket-клиент. Давайте посмотрим, как это все устроено и чем оно нам полезно.
 
Введение

Все мы заметили, как перед новым годом фирма 1С активировалась и выпустила сразу две версии платформы 8.3.27 и 8.5.

 

 

У меня сложилось впечатление, что 8.5 это был отвлекающий маневр.

Посудите сами…

Сколько раз люди писали, что нужны WebSocket’ы?

На зазеркалье в плане задач 8.3.25 впервые мы увидели их в планах, потом они переносились и вновь на зазеркалье был анонс про 8.3.27 и их реализацию.

А новый воздушный интерфейс я не видел, чтобы просили. Думали, что появится новая платформа версии 9 и там все это будет, потом думали, что 1С: Элемент это и есть 9-ка. Но вдруг перед новым годом пошли анонсы, и стало ясно, что интерфейсу быть в 8.3.27.

Выходит 8.3.27, где появились WebSocket’ы, а интерфейса нет.

И в анонсе интерфейса волшебным образом пропало упоминание платформы 8.3.27. Потом, бац и 8.5 с интерфейсом…

 

Сколько людей писали про WebSocket’ы с момента их появления в 8.3.27?

Я не нашел свежих обзоров и снял небольшое видео в праздники.

Посмотреть можно тут -> Пример с WebSocket (В видео я подымаю WebSocket-сервер на NodeJS, подключаюсь к нему с веб-страничек, postman’а и 1С. Шлю сообщения в реальном времени.)

 

А сколько писанины, видосиков, постов, приколов про новый интерфейс?

Знаю только одного человека, который про это не знал…

Я собеседовал соискателя через пару дней, как вышла 8.5, и спросил:

- Что из нашумевших новинок в мире 1С сейчас у всех на слуху?

Он сказал, что не следит за новинками и не в курсе, так как у него нет времени на это…

 

 

По большому счету, достаточно было уделить 10-30 минут, чтобы быть в курсе и понять, что в новом интерфейсе «продумана каждая деталь», но не сам интерфейс, и забыть, как страшный сон, до следующих релизов.

 

Искренне надеюсь, что 8.5 не постигнет участь 8.4.

Кто не в курсе, 8.4 выходила в бете и в ней упоминалась модульность, но так и не вышла в релиз. Хотя я слышал, что кто-то даже внедрял у себя 8.4.

 

 

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

«То, что ждали, растворилось в хайпе того, что не ждали»

 

Давайте поговорим наконец о важном, но все по порядку.

 

 
HTTP-Сервисы vs WebSocket

Ранее мы имели возможность создавать и цепляться к http-сервисам и это удобно.

По сути http-сервис — это запрос-ответ. Клиент просит у сервера информацию, запускается сессия, во время которой сервер возвращает запрошенные данные.

Это удобно до той поры, пока вам не нужно организовать данные в реальном времени.

Вы, конечно, можете в цикле постоянно опрашивать о изменениях, но это похоже на DDoS-атаку своими силами.

 

 

Также вам может понадобиться двухсторонний обмен.

Можно, конечно, поднять два веб-сервера или использовать КД, но что делать, если у вас клиентами выступают: вебсайты, телефоны и прочие клиенты?

И вот тут начинают появляться различные прокладки…

 

Представьте: Вы сидите за длинным столом и вам захотелось салат с другого края стола. Вы просите у соседа, чтобы он попросил салат. Он просит у соседа и так до тех пор, пока запрос не дойдет до человека, сидящего рядом с салатом. И вот салатница идет по рукам и каждый может попросить себе пару ложечек. А до вас уже может и пустая салатница доехать (наверное, все хоть раз сталкивались с проблемой остатков).

 

 

Но наконец то и на улице 1С платформы праздник, на сцену выходят WebSocket’ы.

В отличии от HTTP-сервисов, WebSocket’ы позволяют создать соединение и держать его постоянно открытым, организуя двухсторонний обмен. Клиенты могут слать сообщения на сервер, сервер клиентам и все это практически онлайн.

 

 

Представьте: Мальчик подходит к ребятам, играющим в футбол, и спрашивает:

-Можно с вами?

В ответ:

-Да.

И вот они уже играют в футбол все вместе.

Кто-то уходит, кто-то приходит. Игра идет до тех пор, пока есть желающие играть и есть футбольное поле.

 

 

Фиксируем:

Протокол для обмена сообщениями между клиентом и сервером, используя постоянное соединение.

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

 

Что схожего у HTTP-Сервисов и WebSocket?

  • Оба протокола имеют возможность шифровать трафик (http[S], ws[S])
  • Протоколы имеют схожие параметры в том числе и заголовки.

 

 

  • Оба кроссплатформенные. Простите меня, но не мог не плюнуть в COM.
  • У HTTP протокола есть коды ответов, которые хранят в себе информацию по ошибкам или удачном выполнении запроса. У WebSocket есть коды ошибок и коды закрытия.

Например, при закрытии коды:

1000 - нормальное закрытие

1005 - ошибка не указана

 

 

Коды WebSockets

Коды HTTP

 

  • Оба с данного момента поддерживает 1С Платформа

 

Что же сулят нам WebSocket’ы?

У нас появилась возможность подымать свои WebSocket-серверы и цепляться к ним при помощи платформы 1С, из коробки.

Мы можем цепляться к брокерам и прочим сервисам по данному протоколу.

У нас открываются огромные возможности для интеграций, при этом нам наконец то не нужны внешние компоненты, «поле HTML документа» и прочие прокладки.

Это не значит, что http-сервисы пора выкидывать на помойку. Нет, это значит, что те задачи, которые мы решали при помощи костылей, можно теперь решать по-людски.

 

 
Как устроены WebSocket’ы в платформе 1С

Изменения в платформе, связанные с WebSocket-клиентом

 

 

  • У нас появилась возможность создавать WebSocket-клиенты на стороне клиентской части и на стороне кластера серверов. Сделано это специально, так как на клиенте, либо на сервере по каким-то причинам может отсутствовать доступ к Web Socket соединению. И этого вполне достаточно для организации различных интеграций. Уже есть готовые WebSocket-серверы для различных сервисов, брокеров и другого программного обеспечения. Да и свой WebSocket-сервер поднять труда не составит, главное продумать логику. Я покажу простой вариант, как это сделать.

Тут я кстати вижу огромные плюсы для нас. У нас есть возможность покачать навыки в других языках создавая backend.

 

  • В СУБД у нас появилась таблица [_WebSocketClients]:

 

 

Где:

[_ID] – идентификатор таблицы в СУБД.

[_WSCKey] – Web Socket Connect Key. Уникальный ключ, идентификатор соединения

[_MetadataID] – идентификатор в дереве метаданных

[_ServerURL] – сервер подключения к WebSocket. Адрес WebSocket-сервера

[_Predefined] – предопределенный или нет

[_ConnectionParameters] – дополнительные параметры используемые при подключении

[_IBUserName] – пользователь информационной базы, под которым запущено соединение

[_AutoConnect] – автоподключение. Указывает на необходимость автоматического подключения к серверу и восстановления подключения после разрыва соединения

 

  • Мы можем создавать WebSocket-клиент как в конфигурации так и программно(в документации ИТС называют динамическое добавление). Клиенты созданные в дереве конфигурации, доступны только на стороне кластера серверов

 

В дереве метаданных появился пункт WebSocket-клиенты:

 

 

В расширении тоже появилась возможность добавлять WebSocket-клиенты:

 

 

В модуле WebSocket-клиента мы можем создать обработчики:

 

 

В предметах отладки появились WebSocket и WebSocket(файловый вариант):

 

 

Давайте добавим предопределенный WebSocket-клиент в конфигураторе и посмотрим, что изменится в СУБД.

Я специально не установил галочку «Подключаться автоматически»:

 

 

Данные в СУБД:

 

 

Обратите внимание на WSCKey. Когда мы добавляем WebSocket-клиент в конфигураторе и делаем клиент предопределенным, ключ заполняется автоматически.

 

  • В платформе появилась служебная обработка «Управление WebSocket-клиентами». Она позволяет создавать, удалять и изменять WebSocket-клиенты. Рассказывать про нее нет смысла, моя цель разобрать как это все работает внутри.

 

 

Если данную обработку хотите забрать для исследований, тогда можно воспользоваться, например, вот этой обработкой:

 

 

На ИТС выделяют три способа создания WebSocket-клиентов:

  1. Динамические соединения на стороне клиентского приложения
  2. Динамические соединения на стороне кластера серверов
  3. WebSocket-клиенты на стороне кластера серверов

Схема работы с WebSocket (источник):

 

 

Эксперименты

Создаем WebSocket-сервер

Для демонстрации «WebSocket-клиентов на стороне кластера серверов» я подыму свой WebSocket-сервер на NodeJS.

Нам понадобятся:

  1. VSC. Качаем и устанавливаем: https://code.visualstudio.com/ 
  2. Nodejs. Качаем и устанавливаем: https://nodejs.org/
  3. Установщик пакетов npm.
  4. Библиотека ws и код WebSocket сервера.
  5. 1C 8.3.27 или 8.5
  6. Postman. Необязательно но желательно. Скачать можно тут -> https://www.postman.com/downloads/

 

Установка по порядку:

Команду установки npm можно выполнить в VSC.

Нужно лишь запустить терминал:

 

 

И выполнить команду: npm install -g npm

  • 4 Пункт. Создаем папку у себя, в которой будет WebSocket-сервер.

В этой папке открываем VSC и запускаем терминал как в пункте 3.

Выполняем команду: npm i ws

После выполнения в папке появятся файлы с описанием библиотек и папка с библиотекой ws.

 

 

 
Создаем файл index.js и помещаем в него следующий код:

 

// Подключаем библиотеку ws
const WebSocket = require("ws");

// Запускаем WebSocket сервер локально на порту 3001
const server = new WebSocket.Server({
  port: 3001,
});

// Создаем массив для хранения всех сообщений
const messages = [];

// Делаем обработчик для подключения
server.on("connection", (ws) => {
  // Отправляем клиенту все сообщения в формате JSON, которые уже были отправлены
  console.log("Client connected"); // информируем в консоли о новом подключении
  ws.send(JSON.stringify(messages));

  // Делаем обработчик отправки сообщения
  ws.on("message", (message) => {
    // Получаем сообщение в виде строки
    message = message.toString();
    // Добавляем сообщение в конец массива
    messages.push(message);
    // Отправляем все сообщения в формате JSON всем клиентам
    server.clients.forEach((client) => {
      client.send(JSON.stringify(messages));
    });
  });

  // Делаем обработчик при закрытии сессии клиентом
  ws.on("close", () => {
    console.log("Client disconnected"); // информация в консоли о отключении клиента
    // Отправляем всем клиентам что кто-то отключился
    server.clients.forEach((client) => {
      client.send("Client disconnected");
    });
  });
});

// Делаем обработчик ошибок
server.on("error", (error) => {
  console.log(error); // Информация в консоли об ошибке
});

// Делаем обработчик закрытия сессии на сервере
server.on("close", () => {
  // Отправляем всем информацию о закрытии сессии
  server.clients.forEach((client) => {
    client.send("Server closed");
  });

  console.log("Server closed"); //Информация в консоли о закрытии сессии
});

 

        

Выполняем команду запуска WebSocket сервера: node index.js

 

  • 5 пункт. Я рассчитываю, что 1С вы сами скачали и установили. Я лично использую комьюнити версию.
  • 6 пункт. Необязательный, но postman, по-моему, использует большинство. В нем есть возможность подключаться к WebSocket сессии, а значит мы без труда сможем проверить подключение.

 

Пункты 2,3,4 и 6 были показаны в видео Пример с WebSocket

Если вы все сделали верно, тогда в postman подключение произойдет без ошибок:

 

 

        

Адрес нашего локального сервера: ws://localhost:3001

 

 

Если у вас все получилось, тогда вы увидите следующую картину:

 

 

Создаем WebSocket-клиент на стороне кластера серверов

Добавляем WebSocket-клиент прямо в конфигураторе:

 

 

В модуле прописываем все обработчики.

 
Код модуля:

 

Процедура ПередПодключением(Клиент)
	ВебСокетыКлиентСервер.ПередПодключением(Клиент); 
КонецПроцедуры

Процедура ПриОткрытииСоединения(Соединение)
	ВебСокетыКлиентСервер.ПриОткрытииСоединения(Соединение);   
КонецПроцедуры

Процедура ПриПолученииСообщения(Соединение, Сообщение)
	ВебСокетыКлиентСервер.ПриПолученииСообщения(Соединение, Сообщение);
КонецПроцедуры

Процедура ПриОшибке(Соединение, КодОшибки, Описание)
	ВебСокетыКлиентСервер.ПриОшибке(Соединение, КодОшибки, Описание);
КонецПроцедуры

Процедура ПриЗакрытииСоединения(Соединение, КодЗакрытия)
	ВебСокетыКлиентСервер.ПриЗакрытииСоединения(Соединение, КодЗакрытия);
КонецПроцедуры

 

 

Добавляем модуль, в котором будет происходить общая обработка и запись в регистр с логами:

 

 

 
Код модуля:
Процедура ПередПодключением(Клиент) Экспорт 
	Параметры = ПараметрыКлиента(Клиент); 
	ЗаписатьВЛог("ПередПодключением",Клиент.Ключ,Параметры);	
КонецПроцедуры

Процедура ПриОткрытииСоединения(Соединение) Экспорт
	Параметры = ПараметрыСоединения(Соединение);
	ЗаписатьВЛог("ПриОткрытииСоединения",Соединение.Ключ,Параметры);
КонецПроцедуры

Процедура ПриПолученииСообщения(Соединение, Сообщение) Экспорт 
	Параметры = ПараметрыСоединения(Соединение); 
	Параметры.Вставить("Сообщение",Сообщение);
	ЗаписатьВЛог("ПриПолученииСообщения",Соединение.Ключ,Параметры);
КонецПроцедуры

Процедура ПриОшибке(Соединение, КодОшибки, Описание) Экспорт  
	Параметры = ПараметрыСоединения(Соединение); 
	Параметры.Вставить("Код",КодОшибки);
	Параметры.Вставить("Описание",Описание);
	ЗаписатьВЛог("ПриОшибке",Соединение.Ключ,Параметры);
КонецПроцедуры    

Процедура ПриЗакрытииСоединения(Соединение, КодЗакрытия) Экспорт 	
	Параметры = ПараметрыСоединения(Соединение); 
	Параметры.Вставить("Код",КодЗакрытия);
	ЗаписатьВЛог("ПриЗакрытииСоединения",Соединение.Ключ,Параметры);
КонецПроцедуры



Процедура ОбработчикОткрытия(Соединение) Экспорт 
	Параметры = ПараметрыСоединения(Соединение);
	ЗаписатьВЛог("ОбработчикОткрытия",Соединение.Ключ,Параметры);	
КонецПроцедуры   

Процедура ОбработчикПолученияСообщения(Соединение, Сообщение) Экспорт
	Параметры = ПараметрыСоединения(Соединение); 
	Параметры.Вставить("Сообщение",Сообщение);
	ЗаписатьВЛог("ОбработчикПолученияСообщения",Соединение.Ключ,Параметры);
КонецПроцедуры    

Процедура ОбработчикОшибки(Соединение, КодОшибки, Описание) Экспорт  
	Параметры = ПараметрыСоединения(Соединение); 
	Параметры.Вставить("Код",КодОшибки);
	Параметры.Вставить("Описание",Описание);
	ЗаписатьВЛог("ОбработчикОшибки",Соединение.Ключ,Параметры);
КонецПроцедуры 

Процедура ОбработчикЗакрытия(Соединение, КодЗакрытия) Экспорт  
	Параметры = ПараметрыСоединения(Соединение); 
	Параметры.Вставить("Код",КодЗакрытия);
	ЗаписатьВЛог("ОбработчикЗакрытия",Соединение.Ключ,Параметры);	
КонецПроцедуры 



#Область СборПараметров

Функция ПараметрыКлиента(Клиент)
	
	СтруктураКлиент = Новый Структура;
	СтруктураКлиент.Вставить("URLСервера", Клиент.URLСервера);
	СтруктураКлиент.Вставить("ИмяПользователяИнформационнойБазы", Клиент.ИмяПользователяИнформационнойБазы);
	СтруктураКлиент.Вставить("Заголовки", Клиент.ПараметрыСоединения.Заголовки);
	СтруктураКлиент.Вставить("МетаданныеИмя", Клиент.Метаданные.Имя);
	//СтруктураКлиент.Вставить("МетаданныеЗаголовки", Клиент.Метаданные.Заголовки);
	СтруктураКлиент.Вставить("МетаданныеURLСервера", Клиент.Метаданные.URLСервера);
	КлиентJSON = ЗаписатьДанныеВJSON(,СтруктураКлиент).Результат;
	
	Параметры = Новый Структура;
	Параметры.Вставить("Клиент",КлиентJSON);
	
	Возврат Параметры;
КонецФункции

Функция ПараметрыСоединения(Соединение)
	
	СтруктураСоединение = Новый Структура;
	СтруктураСоединение.Вставить("URLСервера", Соединение.URLСервера);
	СтруктураСоединение.Вставить("ИмяПользователяИнформационнойБазы", Соединение.ИмяПользователяИнформационнойБазы);  
	СтруктураСоединение.Вставить("Модуль", Соединение.Обработчики.Модуль); 
	СтруктураСоединение.Вставить("ОбработчикЗакрытияСоединения", Соединение.Обработчики.ОбработчикЗакрытияСоединения); 
	СтруктураСоединение.Вставить("ОбработчикОткрытияСоединения", Соединение.Обработчики.ОбработчикОткрытияСоединения); 
	СтруктураСоединение.Вставить("ОбработчикОшибки", Соединение.Обработчики.ОбработчикОшибки); 
	СтруктураСоединение.Вставить("ОбработчикПолученияСообщения", Соединение.Обработчики.ОбработчикПолученияСообщения); 
	СтруктураСоединение.Вставить("Заголовки", Соединение.Параметры.Заголовки);

	СоединениеJSON = ЗаписатьДанныеВJSON(,СтруктураСоединение).Результат;
	
	Параметры = Новый Структура;
	Параметры.Вставить("Соединение",СоединениеJSON);
	Возврат Параметры;
КонецФункции

#КонецОбласти
	

Процедура ЗаписатьВЛог(Обработчик,Ключ,Параметры=Неопределено)
	Дата = ТекущаяДатаСеанса();
	Если Не ЗначениеЗаполнено(Параметры) Тогда 
		Параметры = Новый Структура;
	КонецЕсли;	
	
	
	БылаОшибка = Ложь;
	Попытка	
		               
		НаборЗаписей = РегистрыСведений.СобытияВебСокеты.СоздатьНаборЗаписей();  			
		НаборЗаписей.Отбор.Дата.Установить(Дата);
		НаборЗаписей.Отбор.Обработчик.Установить(Обработчик);
		НаборЗаписей.Отбор.Ключ.Установить(Ключ);
		НаборЗаписей.Прочитать();	

		Если НаборЗаписей.Количество() = 0 Тогда
			НоваяЗаписьРегистра = НаборЗаписей.Добавить();
			
			НоваяЗаписьРегистра.Дата 		= Дата;
			НоваяЗаписьРегистра.Обработчик 	= Обработчик;
			НоваяЗаписьРегистра.Ключ 		= Ключ;	
		Иначе
			НоваяЗаписьРегистра = НаборЗаписей[0];
		КонецЕсли;
				
       	Для Каждого текПараметр Из Параметры Цикл 
				
			НоваяЗаписьРегистра[текПараметр.Ключ] = текПараметр.Значение;	
			
		КонецЦикла;
		
		
		НаборЗаписей.Записать(); 
					
	Исключение	
		
		БылаОшибка = Истина;
		ТекстОшибки = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
			
	КонецПопытки;

	Если БылаОшибка Тогда 
		ЗаписьЖурналаРегистрации("Ошибка",УровеньЖурналаРегистрации.Ошибка,,,ТекстОшибки);
	КонецЕсли;	
	
КонецПроцедуры


#Область СериализацияДесериализацияДанных 

// Возвращает чаще всего используемые ПараметрыЗаписиJSON (JSONWriterSettings)
// 
// Возвращаемое значение:
//  Результат - Структура - Стандартное заполнение
//
Функция СтандартныеПараметрыJSON() Экспорт
	
	Результат = Новый Структура;
	Результат.Вставить("ПереносСтрок",			ПереносСтрокJSON.Авто);
	Результат.Вставить("СимволОтступа",			" ");
	Результат.Вставить("ИспользоватьДвойныеКавычки",	Истина);
	Результат.Вставить("ЭкранированиеСимволов",		ЭкранированиеСимволовJSON.Нет);
	Результат.Вставить("ЭкранироватьАмперсанд",		Ложь);
	Результат.Вставить("ЭкранироватьОдинарныеКавычки",	Ложь);
	Результат.Вставить("ЭкранироватьРазделителиСтрок",	Ложь);
	Результат.Вставить("ЭкранироватьУгловыеСкобки",		Ложь);
	Результат.Вставить("ЭкранироватьСлеш",			Ложь);	
	
	Возврат Результат;		
	
КонецФункции

// Преобразует данные в формат JSON
//
// Параметры:
//  ВходящиеПараметры	 - Структура - Параметры формирование JSON
//  ВходныеДанные		 - ЛюбоеЗначение - Данные которые нужно перевести в JSON
// 
// Возвращаемое значение:
//  Результат - Структура
//  	Отработал - Булево - Выполнено или нет
//  	ТекстОшибки - Строка - Текст ошибки если функция отработала с ошибкой
//  	Результат - Строка - JSON
//
Функция ЗаписатьДанныеВJSON(Знач ВходящиеПараметры = Неопределено, Знач ВходныеДанные = "") Экспорт
	
	Результат = Новый Структура("Результат, Отработал, ТекстОшибки", "", Истина, "");
	
	Если ВходящиеПараметры = Неопределено Тогда 
		ВходящиеПараметры = СтандартныеПараметрыJSON(); 	
	КонецЕсли;	
	
	ПараметрыJSON	= Новый ПараметрыЗаписиJSON(ВходящиеПараметры.ПереносСтрок,
		ВходящиеПараметры.СимволОтступа,
		ВходящиеПараметры.ИспользоватьДвойныеКавычки,
		ВходящиеПараметры.ЭкранированиеСимволов,
		ВходящиеПараметры.ЭкранироватьУгловыеСкобки,
		ВходящиеПараметры.ЭкранироватьРазделителиСтрок,
		ВходящиеПараметры.ЭкранироватьАмперсанд,
		ВходящиеПараметры.ЭкранироватьОдинарныеКавычки,
		ВходящиеПараметры.ЭкранироватьСлеш);
	
	
	Попытка
		ЗаписьJSON						= Новый ЗаписьJSON;
		ЗаписьJSON.ПроверятьСтруктуру 	= Истина;
		ЗаписьJSON.УстановитьСтроку(ПараметрыJSON);	
		ЗаписатьJSON(ЗаписьJSON, ВходныеДанные,,"ФункцияПреобразованияЗаписи",ВебСокетыКлиентСервер);
		Результат.Вставить("Результат",	ЗаписьJSON.Закрыть());
	Исключение
		Результат.Отработал 			= Ложь;
		Результат.ТекстОшибки 			= ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
	КонецПопытки;
	
	Возврат Результат;
	
КонецФункции	

Функция ФункцияПреобразованияЗаписи(Свойство, Значение, ДополнительныеПараметры, Отказ) Экспорт
    Если ТипЗнч(Значение) = Тип("СписокЗначений") Тогда  
        МассивЗначений = Значение.ВыгрузитьЗначения();
        Значение = МассивЗначений;
    КонецЕсли;	
КонецФункции

// Десериализует строку JSON в формат данных 1С
//
// Параметры:
//  СтрокаJSON - Строка - JSON
// 
// Возвращаемое значение:
//  Результат - Структура
//  	Отработал - Булево - Выполнено или нет
//  	ТекстОшибки - Строка - Текст ошибки если функция отработала с ошибкой
//  	Результат - ЛюбоеЗначение - Данные в формате 1С
//
Функция ЧтениеДанныхИзJSON(Знач СтрокаJSON) Экспорт
	
	Результат = Новый Структура("Отработал, ТекстОшибки", Истина, "");
						
	Попытка
		ЧтениеJSON 				= Новый ЧтениеJSON;
		ЧтениеJSON.УстановитьСтроку(СтрокаJSON);
		Результат.Вставить("Результат", ПрочитатьJSON(ЧтениеJSON));
	Исключение
		Результат.Отработал 	= Ложь;
		Результат.ТекстОшибки 	= ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
	КонецПопытки;
	
	Возврат Результат;
	
КонецФункции

// Десериализует поток JSON в формат данных 1С
//
// Параметры:
//  ПотокJSON	 - Поток -
// 
// Возвращаемое значение:
//  Результат - Структура
// 		Отработал - Булево - Выполнено или нет
//  	ТекстОшибки - Строка - Текст ошибки если функция отработала с ошибкой
//  	Результат - ЛюбоеЗначение - Данные в формате 1С
//
Функция ЧтениеПотокаИзJSON(Знач ПотокJSON) Экспорт
	
	Результат = Новый Структура("Отработал, ТекстОшибки", Истина, "");
					
	Попытка
		ЧтениеJSON 				= Новый ЧтениеJSON;
		ЧтениеJSON.ОткрытьПоток(ПотокJSON);
		Результат.Вставить("Результат", ПрочитатьJSON(ЧтениеJSON));
	Исключение
		Результат.Отработал 	= Ложь;
		Результат.ТекстОшибки 	= ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
	КонецПопытки;
	
	Возврат Результат;
	
КонецФункции

#КонецОбласти

 

 

Еще я добавил регистр для логирования событий на всех обработчиках:

 

 

На этом подготовительные работы в 1С завершены, можно запускать пример.

Запускаем новую обработку из функций для технического специалиста и нажимаем «Подключить»:

 

 

Перед подключением запустился обработчик «ПередПодключением»

Параметры обработчика:

         Клиент – тип WebSocketКлиент

В справке есть ошибка в примере, которая вводит в заблуждение:

 

 

Обработчик вызывается перед попыткой установить соединение. Позволяет изменить настройки соединенияWebSocketКлиента. Тут можно установить и переопределить настройки.

Что можно переопределить:

  • URLСервера
  • ИмяПользователяИнформационнойБазы
  • ПодключатьАвтоматически
  • ? Клиент.ПараметрыСоединения - В справке написано только чтение, но я добавлял заголовки. Ниже пример с доказательством.

 

Пример кода:

Процедура ПередПодключением(Клиент)

   Клиент.URLСервера = "wss://ws.postman-echo.com/raw";
   Клиент.ПараметрыСоединения.Заголовки.Вставить("Authorization","Bearer 123");
   Клиент.ИмяПользователяИнформационнойБазы = "Администратор";

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

 

 

После того как установится соединение с сервером запускается обработчик «ПриОткрытииСоединения»

Параметры обработчика:

         Соединение – тип WebSocketКлиентСоединение

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

 

 

Обработчик «ПриПолученииСообщения»

Параметры обработчика:

         Соединение – тип WebSocketКлиентСоединение

         Сообщение – тип Строка, ДвоичныеДанные

Напоминаю наш WebSocket сервер при подключение присылает все накопленные отправленные ранее сообщения, поэтому мы зацепим еще и этот обработчик.

Сообщений пока не было, поэтому пришел пустой массив:

 

 

А вот теперь предлагаю отправить сообщение из postmana:

 

 

В 1С прилетело сообщение:

 

 

Теперь давайте в обработке нажмем отключить сообщение:

 

 

Обработчик «ПриЗакрытииСоединения»

         Соединение – тип WebSocketКлиентСоединение

         КодЗакрытия – тип Строка

Мы попадаем в обработчик, в котором можем оценить по какой причине произошло закрытие, для этого у нас есть КодЗакрытия.

1005 – в нашем случае говорит о том, что ошибок не было.

 

 

А теперь самое интересное, мы же при закрытии сессии клиента сообщаем всем остальным клиентам, что кто-то покинул сессию.

Смотрим в postman:

 

 

Предлагаю еще раз подключится и отправить сообщение.

После подключения заглядываю в регистр логов, чтобы забрать ключ подключения к сессии и за одно посмотреть на историю:

 

 

 
Я скидал однокнопковую обработку вот с таким кодом:

 

&НаКлиенте
Процедура Команда1(Команда)
	Команда1НаСервере();
КонецПроцедуры

Процедура Команда1НаСервере()
	
	СоединенияПоКлючу = "9f442068-bee0-4472-9376-36896c5ed5fa WebSocketКлиентTest";
	
	ВебСокет = WebSocketКлиентСоединения.ПолучитьСоединение(СоединенияПоКлючу);	
	Если ВебСокет <> Неопределено Тогда 
		ВебСокет.ОтправитьСообщение("Привет, постман! ПолучитьСоединениЕ по ключу");
	КонецЕсли;
	
	ВебСокетЫ = WebSocketКлиентСоединения.ПолучитьСоединения();
	Для Каждого ВебСокет из ВебСокетЫ Цикл
		Если ВебСокет <> Неопределено Тогда
			ВебСокет.ОтправитьСообщение("Привет, постман! ПолучитьСоединениЯ");
		КонецЕсли;	
	КонецЦикла;
	
КонецПроцедуры

 

 

По сути, я попытаюсь найти соединение по ключу и, если найду отправлю сообщение "Привет, постман! ПолучитьСоединениЕ по ключу".

Далее я получу все соединения и отправлю из них сообщение "Привет, постман! ПолучитьСоединениЯ".

Если все пройдет хорошо, тогда postman получит минимум одно сообщение, и мы в 1С получим массив с этими сообщениями.

 

 

Первое есть:

 

 

Второе есть:

 

 

В postman тоже сообщения пришли:

 

 

За кадром остался еще один обработчик...

 

Обработчик «ПриОшибке»

Параметры обработчика:

         Соединение – тип WebSocketКлиентСоединение

         КодОшибки – тип Строка

Описание – тип Строка

Обработчик вызывается в том случае, если при работе соединения произошла какая-либо аварийная ситуация.

Вот, собственно, и все.

 

Итоги:

  • В первой части мы с вами обсудили, что за зверь этот WebSoket.
  • Обсудили подходы у HTTP запросов и у WebSoket’ов.
  • Посмотрели на WebSoket’ы в СУБД.
  • Посмотрели, как можно создать WebSoket’ы.
  • Попользовались новой обработкой.
  • Создали свой WebSoket сервер на NodeJS с использованием библиотеки ws.
  • Подцепились к своему WebSoket серверу при помощи postman и 1С.
  • Разобрали для чего нужны обработчики и нашли неточности в описании.
  • Сделали сквозной пример.

 

Все материалы выложены в GitHub:

WebSocketIn1C на GitHub

 

На этом завершаю первую часть разбора.

Высплюсь и буду готовить вторую ;)

Всем удачи и интересных проектов!

ТОП-5 ИНСТРУМЕНТОВ ДЛЯ РАЗРАБОТЧИКА 1С

Toolkit, DCT, OneDebugger, PrintWizard, DataFormWizard
со скидкой 20% при покупке от 2х решений!


платформа Регистры сведений Интеграция Архитектура GitHub WebSocket WebSockets сервер сервис 8.3.27 DDoS клиент ws wss https http ключ обмен заголовки

См. также

Механизмы платформы 1С Программист Стажер Платформа 1С v8.3 1C:Бухгалтерия Бесплатно (free)

Эта небольшая статья - некоторого рода шпаргалка по файловым потокам: как и зачем с ними работать, какие преимущества это дает.

23.06.2024    10654    bayselonarrend    21    

161

Механизмы платформы 1С Программист Стажер Платформа 1С v8.3 1C:Бухгалтерия Бесплатно (free)

Пример использования «Сервисов интеграции» без подключения к Шине и без обменов.

13.03.2024    7523    dsdred    18    

81

Механизмы платформы 1С Программист Стажер Платформа 1С v8.3 Бесплатно (free)

Все мы используем массивы в своем коде. Это один из первых объектов, который дают ученикам при прохождении обучения программированию. Но умеем ли мы ими пользоваться? В этой статье я хочу показать все методы массива, а также некоторые фишки в работе с массивами.

24.01.2024    25044    YA_418728146    32    

73

Механизмы платформы 1С Программист Бесплатно (free)

Язык программирования 1С содержит много нюансов и особенностей, которые могут приводить к неожиданным для разработчика результатам. Сталкиваясь с ними, программист начинает лучше понимать логику платформы, а значит, быстрее выявлять ошибки и видеть потенциальные узкие места своего кода там, где позже можно было бы ещё долго медитировать с отладчиком в поисках источника проблемы. Мы рассмотрим разные примеры поведения кода 1С. Разберём результаты выполнения и ответим на вопросы «Почему?», «Как же так?» и «Зачем нам это знать?». 

06.10.2023    25694    SeiOkami    48    

136
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. Shmell 547 14.01.25 13:12 Сейчас в теме
Отличная статья! Раскрываются подкопотные детали ) Спасибо )
Evg-Lylyk; cleaner_it; dsdred; +3 Ответить
2. dsdred 3786 14.01.25 13:48 Сейчас в теме
(1) рад, что статья понравилась
3. John_d 5967 14.01.25 16:38 Сейчас в теме
Спасибо, интересно
4. dsdred 3786 14.01.25 16:48 Сейчас в теме
(3) Рад, что статья понравилась.
Вторую часть попробую сделать еще интереснее ;)
5. quazare 3873 14.01.25 18:40 Сейчас в теме
Как думаете, какова судьба сервера взаимодействия, если повсеместно будут внедряться такие «ящики-сокеты», для обмена «всего что угодно»????
6. dsdred 3786 14.01.25 19:00 Сейчас в теме
(5) вопрос интересный. 😁
Скажем так. Малый бизнес не осилит сокеты.
А по остальным, своя ниша, я думаю найдется и там и там. Многое будет зависить от того как будет развиваться система взаимодействия. Я не думаю, что кто-то будет повторять систему взаимодействия, но окошечко онлайн тех суппорта напрашивается.
7. quazare 3873 14.01.25 19:35 Сейчас в теме
(6) Дмитрий, вы хотели написать, что малый бизнес не осилит систему взаимодействия? Сокеты - это как раз что нужно, он системы корпоративных чатов до «ящиков» с прайслистами…..
8. dsdred 3786 14.01.25 19:45 Сейчас в теме
(7) Есть же халявная система взаимодействия(без инсталяции). Её я кстати у среднячков видел.

Сокеты же кто-то должен северную часть написать и поддерживать. Хотя конечно можно тяп ляп... да наверно малый бизнес как раз тяп ляп осилит.

Хм... вот сейчас сижу и думаю для кого система взаимодействия интересна... пока мыслей нет...

Интересный вопрос. Надо подумать. 😁
11. starik-2005 3127 14.01.25 21:38 Сейчас в теме
(8)
Сокеты же
А чем не нравится взаимодействие МП, написанное на 1С, с базой по вебсокету? Прям вот напрашиваеццо...
16. dsdred 3786 15.01.25 05:28 Сейчас в теме
(11) МП - мобильная платформа?
Я хочу кстати и про это в следующих частях поговорить
9. quazare 3873 14.01.25 19:55 Сейчас в теме
(8) в моем понимании - мелкий бизнес -это почти вся массовая оптово-розничная торговля. В моем окружении почти все сидят на веб-сервисах, различных каналах обмена…. Сейчас нет проблем доставить информацию до конечного «потребителя»…

Система взаимодействия и ее аналоги как раз интересны для внутрибазовых рабочих групп-чатов… когда, например, есть база 200 гб - в ней 30-40 пользователей… да, я считаю это «малым» бизнесом
10. dsdred 3786 14.01.25 20:12 Сейчас в теме
(9) малый считаю до 100 человек и обороты до лярда за год. Я зарекся больше с такими конторами работать.

Если кстати систему взаимодействия подружить с вебсокетами, то будет норм профит.
СисВзаимодействия как внутренний чат. ВебСокет как канал например приёмки заказов, заявок. На уровне платформы получаем с веб соккета и маршрутим в сисВзаимодействия. Статусы посылаем через вебСокеты. В общем в паре из них норм тема.

В эту сторону можно что то придумать.
12. starik-2005 3127 14.01.25 21:41 Сейчас в теме
(10)
ВебСокет как канал например приёмки заказов, заявок.
А нужно ли постоянное соединения для этого? Заказы и заявки и по почте ходят отлично. А вот с терминалами сбора данных в режиме онлайн как удаленная клавиатура - уже интересно, т.к. прямо с формы взаимодействие. Сканернул ШК на терминал - он в кассу прилетел пользователю вот прям сразу. Ну и прочее.
15. dsdred 3786 15.01.25 05:27 Сейчас в теме
(12) по заказам остатки в режиме онлайн нужны, поэтому конечно нужно постоянное соединение если по взрослому делать.

Плюсом организация заказов на кухню блюд в ресторанах, кафе.

Да много, что можно придумать.
17. starik-2005 3127 15.01.25 10:23 Сейчас в теме
(15)
по заказам остатки в режиме онлайн нужны
Ну это решается обычными веб-сервисами, когда при попытке отгрузить корзину система проверяет остатки. Вебсокет не решит проблемы конфликтов, т.к. остатки отображаются на момент добавления товара в корзину, а не на момент резервирования.

По поводу кухни - вполне.

Остается проблема написать вебсокет-сервер для организации всего этого безобразия )
21. dsdred 3786 15.01.25 11:08 Сейчас в теме
(17)
Ну это решается обычными веб-сервисами, когда при попытке отгрузить корзину система проверяет остатки. Вебсокет не решит проблемы конфликтов, т.к. остатки отображаются на момент добавления товара в корзину, а не на момент резервирования.


Тут вопрос скорее бизнеса, как они это видят.
Кого-то устраивает что клиент нажимает заказать а ему сообщение "Ты долго думал вот эти товары закончились"
Кому-то нравится чтобы во время заказа у клиента была информация о текущих остатках "много", "3 шт"
25. starik-2005 3127 15.01.25 11:25 Сейчас в теме
(21)
чтобы во время заказа
Ну открыл ты озон или яндыкс, вулдбериз или алик. Ну и видишь там "много"/"3 шт"/"товар закончился". Как это тебе поможет? Это маркетинговая инфа, ибо когда почти не осталось, то ты купишь то, что откладывал. На оптовиков такое не особо действует, а рознице 100% такое в онлайн не нужно - там есть корзина, и некоторые вопросы решаются уже после того, как сборщик пошел собирать и не нашел что-то. Это никого не напрягает, т.к. не угадаешь никогда + человский фактор. Оптовику это важно в момент резервирования, чтобы система зарезервировала то, что смогла, а о том, что не смогла, сообщила. Тут нигде вебсокет не нужен, имха...
26. dsdred 3786 15.01.25 11:28 Сейчас в теме
(25)
Ну открыл ты озон или яндыкс, вулдбериз или алик. Ну и видишь там "много"/"3 шт"/"товар закончился". Как это тебе поможет?


Меня это бодрит и я заказываю быстрее. 😁
13. shchukin_vv 14.01.25 23:07 Сейчас в теме
Интересно, почему сразу в платформу не завезли функционал websocket сервера. Чтобы не было необходимости поднимать какой-либо сторонний сервис. Или это на потом?))
14. dsdred 3786 15.01.25 05:23 Сейчас в теме
(13) Http-сервисы северную часть реализовать было проще. По вебсоккетам довольно таки сложная задача, очень много вариаций с логикой. Я думаю если и сделают то не скоро.
18. starik-2005 3127 15.01.25 10:25 Сейчас в теме
(14)
По вебсоккетам довольно таки сложная задача
Сложная скорее из-за того, что придется соединение поддерживать постоянно. В веб-сервисах это делает веб-сервер, который дергает 1С через компоненту интеграции, но вебсокеты он не умеет, так что придется 1Су что-то такое отдельное в виде агента вебсокет-сервера написать.
19. dsdred 3786 15.01.25 11:06 Сейчас в теме
(18) Я бы на месте 1с это делал не в рамках платформы, а совершенно новый отдельный продукт.
20. starik-2005 3127 15.01.25 11:07 Сейчас в теме
(19)
а совершенно новый отдельный продукт
У них уже есть сервер взаимодействия, который этот "отдельный продукт". Для веб-сокета достаточно отдельного агента.
22. dsdred 3786 15.01.25 11:10 Сейчас в теме
(20) Сервер взаимодействия можно использовать чтобы получить сообщения например С Кролика?
Хотя... Можно в шине сделать коннектор конечно...
23. starik-2005 3127 15.01.25 11:20 Сейчас в теме
(22)
С Кролика
Так кролик - сам себе сервер. 1С тут в качестве клиента. А если хочется напрямую к 1С-серверу по вебсокету законнектиться - тут нужен какой-то агент, фактически отдельный тред, который будет мессаги посылать серверу 1С, дергая его за серверные функции вебсокет-сервера (как это сделано с веб и хттп сервисами).
24. dsdred 3786 15.01.25 11:22 Сейчас в теме
(23) интересно в 1С думают в эту сторону?
27. SirAlex 15.01.25 12:33 Сейчас в теме
Спасибо!
Очень информативно и оформление радует.
28. dsdred 3786 15.01.25 12:40 Сейчас в теме
(27) рад, что статья понравилась.
29. Leits 2 15.01.25 16:46 Сейчас в теме
Автор, красавчик, не первый раз вижу твои статьи - очень удобная и актуальная подача материала.
30. dsdred 3786 15.01.25 16:50 Сейчас в теме
(29) Спасибо за добрые слова!
Очень приятно когда людям полезны статьи. Понимаешь, что не зря потратил силы и время.
31. user612295_death4321 16.01.25 12:03 Сейчас в теме
Статья понравилась. Возможно стоит упомянуть о существовании готовых решений с сервером для веб-сокета? Наверняка что-то подобное уже существует, чем мы постоянно с нуля будем писать код на другом ЯП.

Centrifugo какой нибудь бы справился с задачей сервера веб-сокетов ?
32. dsdred 3786 16.01.25 12:23 Сейчас в теме
(31) Добрый день.
Первоначально задача объяснить как это все работает, а дальше показать какие-то решения.
Моя цель не просто дать ружье, а научить выживать без него ;) Дать кругозор.
Без фундамента дом строить нет смысла.
33. user612295_death4321 16.01.25 12:28 Сейчас в теме
(32)
но задача объяснить как это все работает, а дальше показать какие-то решения.
Моя цель не просто дать ружье, а научить выживать без него ;) Дать кругозор.
Без фундамента дом строить нет смысла.


Жаль. Имхо это была бы очень полезная информация, перечень готовых решений, которые без геморроя можно запустить в каком нибудь докере.
34. dsdred 3786 16.01.25 13:03 Сейчас в теме
(33) подумаю в этом направлении.
35. starik-2005 3127 16.01.25 13:46 Сейчас в теме
(34)
подумаю
Если вы никогда раньше не слышали о Centrifugo, это масштабируемый сервер обмена сообщениями в реальном времени с открытым исходным кодом, написанный на языке Go. Centrifugo может мгновенно доставлять сообщения онлайн-пользователям приложения, подключенным через поддерживаемые транспорты (WebSocket, HTTP-streaming, SSE/EventSource, GRPC, SockJS, и теперь WebTransport). Centrifugo имеет концепцию канала — так что это PUB/SUB сервер.

На днях я прочитал статью о WebTransport API как будущей альтернативе или даже замене WebSockets. Мне стало интересно, что это такое и с чем его едят. Давайте разбираться вместе.
1С опять опоздала?
Прикрепленные файлы:
tolyan_ekb; cam180; dsdred; +3 Ответить
36. dsdred 3786 16.01.25 22:32 Сейчас в теме
(35)
На днях я прочитал статью о WebTransport API как будущей альтернативе или даже замене WebSockets. Мне стало интересно, что это такое и с чем его едят. Давайте разбираться вместе.


Заинтересовали. Допишу по сокетам что запланировал и займусь.
37. zavedeev 18.01.25 00:33 Сейчас в теме
Спасибо, интересно
38. dsdred 3786 18.01.25 10:04 Сейчас в теме
(37) рад, что понравилась статья.
39. user700522_lerner584 23.01.25 08:44 Сейчас в теме
Клиент.ПараметрыСоединения - В справке написано только чтение, но я добавлял заголовки. Ниже пример с доказательством.

и далее в статье пример:
Процедура ПередПодключением(Клиент)

   Клиент.URLСервера = "wss://ws.postman-echo.com/raw";
   Клиент.ПараметрыСоединения.Заголовки.Вставить("Authorization","Bearer 123");
   Клиент.ИмяПользователяИнформационнойБазы = "Администратор";

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


Полагаю, в справке нет ошибки. Когда пишут, что
ПараметрыСоединения
доступно только для чтения, имеется в виду, что нельзя написать:

Клиент.ПараметрыСоединения = <чему-то там>


в этом смысле оно действительно доступно только для чтения, для это как-бы выполняется оператор ".", следующий сразу после. Там читается какой-то следующий (составной) объект, у которого доступны какие-то методы для работы в данном случае.
40. dsdred 3786 23.01.25 08:51 Сейчас в теме
(39) В ваших словах логика есть.
Просто я понимаю вот так -> нельзя редактировать в принципе.
Грубо говоря если нельзя менять паспорт - это значит и фамилию в нем не получится сменить...
А у 1С получается паспорт менять нельзя, но можно штрихкодом фамилию закрасить и сверху написать новую.

П.С. К сожалению фирма 1С так и не научилась писать хорошую документацию с четкими формулировками...
41. Torin99 285 03.02.25 11:22 Сейчас в теме
Интересно было бы посмотреть пример использования веб-сокета в 1с и брокера сообщений. Той же Кафки например...
42. dsdred 3786 03.02.25 11:26 Сейчас в теме
(41) во второй части вряд-ли. Хотя может быть с кроликом...
43. sharindv 04.02.25 14:01 Сейчас в теме
(42) С кроликом пока не получилось через WebSTOMP.
Благодаря вашей статье (точнее WebSocket-серверу на JS) выяснил,
что 1С при отправке режет сообщение по символу с кодом 0:
при отправке "CONNECT"+Символ(0)+"login:guest" приходит только "CONNECT".
Аналогичное поведение и при отправке двоичных данных - режет по нулевому байту.
Бага тестового релиза или фича реализации websocket от 1С - пока непонятно.
P.S.: Символ NUL (код 0) нужен для обозначения конца сообщения в протоколе STOMP
44. dsdred 3786 04.02.25 14:23 Сейчас в теме
(43) Да, я слышал, что есть проблема с двоичными данными
https://bugboard.v8.1c.ru/error/000168270

Пока не ясно 1с ее починит в 8.3.27 или перенесет на 8.3.28
Прикрепленные файлы:
45. gofrom 12.02.25 00:01 Сейчас в теме
(43) Подключиться к кролику можно поверх 2-х протоколов это STOMP и MQTT. Первый текстовый, второй бинарный. STOMP невозможно реализовать из-за неадекватного поведения Символ(0). У вас в примере неправильная команда должна быть примерно такая:
frame = "CONNECT\n"
            + "login: websockets\n";
            + "passcode: rabbitmq\n";
            + "nickname: anonymous\n";
            + "\n\n\0";

NUL должен быть в конце, но все равно не работает( Эту ошибку я не стал регистрировать, надеялся что получится с протоколом MQTT, но там еще хуже, ДвоичныеДанные обрезаются до первого байта 0x00. У меня почти готова реализация MQTT, но применить в 1С ее невозможно и видимо нескоро.
Оставьте свое сообщение