При интеграции 1С с банковским оборудованием (терминалами) разработчики часто сталкиваются со специфическими протоколами обмена. Одна из типичных задач — отправка HEX-строки, описывающей транзакцию, на IP-адрес терминала. Трудности возникают, когда документация к оборудованию требует «отправки данных частями» или асинхронного режима работы, чтобы не блокировать интерфейс пользователя. Разберем, почему стандартные попытки отправить данные «кусками» через COM-объекты приводят к ошибкам и как правильно организовать этот процесс в современных версиях 1С.
Рассмотрим ситуацию, когда разработчик пытается использовать объект WinHttp.WinHttpRequest.5.1 в асинхронном режиме. В коде это выглядит так:
ОбъектHTTP = Новый COMОбъект("WinHttp.WinHttpRequest.5.1");
ОбъектHTTP.Open("POST", "http://192.168.6.66:666", 1); // 1 - признак асинхронности
ОбъектHTTP.Send("ЧастьДанных_1");
ОбъектHTTP.Send("ЧастьДанных_2"); // Здесь возникает ошибка
Причина ошибки заключается в архитектуре протокола HTTP и реализации объекта WinHttpRequest. Метод Send() инициирует полноценную транзакцию HTTP. Как только вызван первый Send(), объект переходит в состояние «отправка/ожидание ответа». Попытка вызвать Send() повторно до завершения текущего цикла «запрос-ответ» вызывает исключение, так как объект занят. HTTP-протокол не предполагает «дозаписи» данных в один запрос через несколько вызовов метода отправки в прикладном коде.
Если терминал требует отправки данных частями по 32 символа, это может означать одно из двух:
Send().В современной платформе 1С (8.3) для реализации асинхронности не нужно использовать флаги COM-объектов. Лучшая практика — выполнение синхронного запроса внутри фонового задания. Это позволяет не «вешать» клиентский сеанс и корректно дождаться ответа от оборудования.
Разберем алгоритм по шагам:
ПодключитьОбработчикОжидания для проверки состояния задания.Пример кода для фонового задания:
// В серверном модуле
Функция ОтправитьДанныеНаТерминал(ПараметрыЗапроса) Экспорт
Соединение = Новый HTTPСоединение(ПараметрыЗапроса.IP, ПараметрыЗапроса.Порт, , , , 30);
Заголовки = Новый Соответствие();
Заголовки.Вставить("Content-Type", "application/octet-stream");
// Преобразуем HEX-строку в ДвоичныеДанные
ДанныеДляОтправки = ПолучитьДвоичныеДанныеИзГексСтроки(ПараметрыЗапроса.ТелоЗапроса);
ЗапросHTTP = Новый HTTPЗапрос("/", Заголовки);
ЗапросHTTP.УстановитьТелоИзДвоичныхДанных(ДанныеДляОтправки);
Попытка
Ответ = Соединение.ОтправитьДляОбъекта(ЗапросHTTP, "POST");
Возврат Ответ.ПолучитьТелоКакСтроку();
Исключение
Возврат "Ошибка: " + ОписаниеОшибки();
КонецПопытки;
КонецФункции
Если по каким-то причинам необходимо использовать именно WinHttpRequest в асинхронном режиме, мы должны организовать цикл ожидания ответа в 1С. Поскольку 1С не умеет напрямую перехватывать события (Events) COM-объектов, мы будем опрашивать свойство ReadyState или Status.
Проанализируем пример реализации ожидания:
ОбъектHTTP = Новый COMОбъект("WinHttp.WinHttpRequest.5.1");
ОбъектHTTP.Open("POST", "http://192.168.6.66:666", 1);
ОбъектHTTP.Send(ПолнаяHEXСтрока);
// Вместо второго Send, запускаем ожидание
Таймаут = 30; // секунд
Начало = ТекущаяДата();
Пока ОбъектHTTP.ReadyState <> 4 И (ТекущаяДата() - Начало) < Таймаут Цикл
// Важно: в управляемых формах здесь нельзя просто зациклить,
// лучше использовать ОбработчикОжидания.
// Для простого примера в обработке:
#Если Клиент Тогда
ОбработкаПрерыванияПользователя();
#КонецЕсли
КонецЦикла;
Если ОбъектHTTP.ReadyState = 4 Тогда
Результат = ОбъектHTTP.ResponseText;
Иначе
Сообщить("Превышено время ожидания терминала");
КонецЕсли;
1. Перевод HEX в Двоичные данные. Терминалы крайне чувствительны к кодировкам. Если вы отправите HEX как обычную строку, 1С может преобразовать её в UTF-8, что изменит байт-код. Проанализируйте документацию: если требуется «бинарный вид», обязательно используйте объект ДвоичныеДанные. Для конвертации HEX-строки (например, "040001...") в массив байтов используйте методы Сред в цикле с преобразованием через ЧислоИзШестнадцатеричнойСтроки().
2. Длина сообщения. Как упоминалось в обсуждении, первые 8 знаков часто описывают длину сообщения. Если вы формируете их вручную, убедитесь, что расчет длины совпадает с реальным объемом передаваемых данных после конвертации в байты.
3. Проблема «кусков» и TCP-сокетов. Если терминал действительно разрывает соединение при попытке отправить всё одним пакетом (что бывает крайне редко для HTTP), выясните, не используется ли там «Raw TCP». Для работы с «сырыми» сокетами в 1С лучше использовать внешние компоненты или обертки над .NET объектом System.Net.Sockets.TcpClient. HTTP-объекты (как встроенные, так и COM) всегда добавляют свои заголовки (Host, Content-Length, User-Agent), которые терминал может не понять.
4. Таймауты. При работе с эквайрингом стандартные 30 секунд могут быть недостаточными (особенно при ожидании ввода пин-кода пользователем). Рекомендуем устанавливать таймаут на уровне 60–120 секунд. В объекте WinHttpRequest это делается методом SetTimeouts, а в HTTPСоединение — параметром конструктора.
Подводя итог: откажитесь от попыток слать данные несколькими вызовами Send(). Соберите пакет целиком, оберните его в фоновое задание и используйте стандартные механизмы HTTPСоединение для надежной и современной интеграции.