Уникальность и борьба с висящими сеансами БД 1С

Если возникла ситуация, что произошел несанкционированный сбой в сети и после перезагрузки остается на  висеть сеанс (хотя на самом деле пользователь
не подключен и сеанс неактивен), то появится диалоговое окно с запросом отключить или нет предыдущий сеанс.
//!!! Для обычного приложения версии 8.2
//Размещать код следует в начало Процедуры ПриНачалеРаботыСистемы модуля обычного приложения.
//Принцип работы - Если возникла ситуация, что произошел несанкционированный сбой в сети и после перезагрузки остается на //сервере висеть сеанс (хотя на самом деле пользователь
//не подключен и сеанс неактивен), то появится диалоговое окно с запросом отключить или нет предыдущий сеанс.
//ПО ПРОСЬБЕ вставлю фрагмент, кот-ый автоматически определит Имя БД, ИмяСервера //********** СтрокаСоединения = СтрокаСоединенияИнформационнойБазы(); Если СтрЧислоВхождений(ВРег(СтрокаСоединения), "FILE=") Тогда Возврат; КонецЕсли; СтрДлина = СтрДлина(СтрокаСоединения); // Ищем имя сервера СтрокаСервера = ВыделитьПодстрокуСтрокиЗапуска(СтрокаСоединения, "Srvr="); ИмяБД = ВыделитьПодстрокуСтрокиЗапуска(СтрокаСоединения, "Ref="); // Если в имени сервера присутствует имя порта, то выделяем его РазделительПорта = Найти(СтрокаСервера, ":"); Если РазделительПорта > 0 Тогда ИмяСервера = Сред(СтрокаСервера, 1, (РазделительПорта - 1)); НомерПортаКластера = Сред(СтрокаСервера, (РазделительПорта + 1)); Иначе ИмяСервера = СтрокаСервера; ком = Новый COMОбъект("v81.COMConnector"); НомерПортаКластера = ком.RMngrPortDefault; КонецЕсли; //*******
ПользовательИБ = ПользователиИнформационнойБазы.ТекущийПользователь();
ПризнакСеанса = 0;
//для обеспечения невозможности повторного входа пользователя, который уже присутствует в БД
//что делается:
ТекущийНомерСоединения=НомерСоединенияИнформационнойБазы();
УникальныйИдентификаторПользователя=ПользователиИнформационнойБазы.ТекущийПользователь().УникальныйИдентификатор;
ТекущееСоединениеИмяКомпьютера = ИмяКомпьютера();
ServerName = ИмяСервера; //Имя Вашего сервера 1С
connector = новый comОбъект("V82.COMConnector");
AgentConnection = Connector.ConnectAgent(ServerName);
МассивКластеров = AgentConnection.GetClusters();
//определяем кластёр как описание типов
Для каждого Кластер ИЗ МассивКластеров Цикл
Если (Кластер.HostName = ИмяСервера) и (Кластер.SyncPort=НомерПортаКластера) тогда КластерТекущейБД = Кластер; //Думаю, что имя сервера будет совпадать с именем компа :)
   // Сообщить(Кластер.HostName);
КонецЕсли;
Прервать;
КонецЦикла;
//Доступ на просмотр сеансов
AgentConnection.Authenticate(КластерТекущейБД,"Администратор","Пароль");
//Для получения ВСЕХ тек.сеансов для данной БД - для инфо.. потом закомм и заодно БД как описание типов
МассивСеансов = AgentConnection.GetSessions(КластерТекущейБД);    
Для каждого Сеанс ИЗ МассивСеансов Цикл
Если (Сеанс.Infobase.Name = ИмяБД) и (Сеанс.UserName = ПользователиИнформационнойБазы.ТекущийПользователь().Имя) тогда
     ОписаниеБД = Сеанс.Infobase;
КонецЕсли;
КонецЦикла;
// Для случаев с висящими сеансами
МассивВсехСессийБД = AgentConnection.GetInfoBaseSessions(КластерТекущейБД,ОписаниеБД);   
Для каждого СессияБД ИЗ МассивВсехСессийБД Цикл
Если //(СессияБД.Host = ТекущееСоединениеИмяКомпьютера) И //ЭТО ЕСЛИ НАДО ИНИЦИАЛИЗАЦИЯ ПО КОМПУ
     (СессияБД.UserName = ПользователиИнформационнойБазы.ТекущийПользователь().Имя) И
     (СессияБД.Connection.Application = "1CV8")
тогда
       Если СессияБД.Connection.ConnID <> ТекущийНомерСоединения
       тогда
Режим = РежимДиалогаВопрос.ДаНет;
           Текст = "Сеанс данного пользователя активен! " + СессияБД.UserName +
                    "| Закрыть предыдущий сеанс? При сбое в сети нажмите <<Да>>";
           Ответ = Вопрос(Текст,Режим,0);
            Если Ответ = КодВозвратаДиалога.Да тогда
                AgentConnection.TerminateSession(КластерТекущейБД,СессияБД);
           КонецЕсли;
           Если Ответ = КодВозвратаДиалога.Нет тогда
         ПризнакСеанса = 1;
    Предупреждение("Внимание! Активно несколько сеансов для одного пользователя");
   КонецЕсли;
      КонецЕсли;
    //если просто висяк,тада безоговорочно закроет на серваке висячий сеанс. проверить
  Сообщить("НомерСоединения: " + СессияБД.Connection.ConnID + " Номер сеанса: " + СессияБД.SessionID);//Сеансы
КонецЕсли;
КонецЦикла;
//а если с другого компа под этим ползуном, ЗАКРЫВАТЬ БУДЕТ ГОСТЯ
//Этот фрагмент кода можно не включать, если Вам нужно просто закрыть висящий сеанс.
МассивСоединений=ПолучитьСоединенияИнформационнойБазы();
Для Каждого ТекСоединение Из МассивСоединений Цикл
Сообщить(ТекСоединение.НомерСоединения);
Если (ТекСоединение.ИмяПриложения="1CV8")                
и(НЕ ТекСоединение.НомерСоединения=ТекущийНомерСоединения)
и(НЕ ТекСоединение.Пользователь=неопределено)        
и(ТекСоединение.Пользователь.УникальныйИдентификатор=УникальныйИдентификаторПользователя)
и(ПризнакСеанса = 0)
//и(НЕ СессияБД.Host = ТекущееСоединениеИмяКомпьютера)
//и(ТекСоединение.Пользователь.ИмяКомпьютера = ТекущееСоединениеИмяКомпьютера)
тогда
   Предупреждение("Пользователь с таким именем уже выполнил вход ! Имя ПК >> " + СессияБД.Host);
        ЗавершитьРаботуСистемы(Ложь);
КонецЕсли;   
КонецЦикла;       

По просьбе — И еще добавить в Модуль новую функцию

// Функция выделяет необходимые части из строки запуска 
// 
Функция ВыделитьПодстрокуСтрокиЗапуска(СтрокаЗапуска, КлючПодстроки) Экспорт 
НомерПервогоСимвола = Найти(ВРег(СтрокаЗапуска), ВРег(КлючПодстроки)) + СтрДлина(КлючПодстроки); 
Для Счетчик = НомерПервогоСимвола По СтрДлина(СтрокаЗапуска) Цикл 
   Если Сред(СтрокаЗапуска, Счетчик, 1) = ";" Тогда 
       НомерПоследнегоСимвола = Счетчик - 1; 
       Прервать; 
   КонецЕсли; 
КонецЦикла; 
// Получаем искомую подстроку 
ИскомаяПодстрока = Сред(СтрокаЗапуска, НомерПервогоСимвола, (НомерПоследнегоСимвола - НомерПервогоСимвола + 1)); 
Если Сред(ИскомаяПодстрока, 1, 1) = """" Тогда 
// Если подстрока в кавычках, то выделяем их 
ИскомаяПодстрока = Сред(ИскомаяПодстрока, 2, (СтрДлина(ИскомаяПодстрока) - 2)); 
КонецЕсли; 
Возврат ИскомаяПодстрока;

КонецФункции // ВыделитьПодстроку()

13 Comments

  1. Гость

    отформатируйте текст статьи

    Reply
  2. g26516

    А для 1с7.7 подойдет?

    Reply
  3. qvvert

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

    Есть какая то возможность определить простой соединения? Как я видел в консоли — в зависшем соединении — однофигово дата последней активности довольно актуальная ( : Скажем пользователь подвис 1 час назад последняя активность соединения минуту назад

    Reply
  4. nikitin19819

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

    Reply
  5. soda

    А как проверить активность пользователей, если в системе есть что-то связанное с обработкой ожидания. например напоминалка, соотщалка…

    Reply
  6. nikitin19819

    (1) вася, более-менее навел красоту 🙂

    Reply
  7. igo1

    Спасибо, но еще неплохо бы добавить след код, ну прописать полученные данные где надо

    СтрокаСоединения = СтрокаСоединенияИнформационнойБазы();
    Если СтрЧислоВхождений(ВРег(СтрокаСоединения), «FILE=») Тогда
    Возврат;
    КонецЕсли;
    
    СтрДлина = СтрДлина(СтрокаСоединения);
    
    // Ищем имя сервера
    СтрокаСервера = ВыделитьПодстрокуСтрокиЗапуска(СтрокаСоединения, «Srvr=»);
    // Если в имени сервера присутствует имя порта, то выделяем его
    РазделительПорта = Найти(СтрокаСервера, «:»);
    Если РазделительПорта > 0 Тогда
    ИмяСервера = Сред(СтрокаСервера, 1, (РазделительПорта — 1));
    НомерПортаКластера = Сред(СтрокаСервера, (РазделительПорта + 1));
    Иначе
    ИмяСервера = СтрокаСервера;
    ком = Новый COMОбъект(«v81.COMConnector»);
    НомерПортаКластера = ком.RMngrPortDefault;
    КонецЕсли;
    
    
    
    
    
    // Функция выделяет необходимые части из строки запуска
    //
    Функция ВыделитьПодстрокуСтрокиЗапуска(СтрокаЗапуска, КлючПодстроки) Экспорт
    
    НомерПервогоСимвола = Найти(ВРег(СтрокаЗапуска), ВРег(КлючПодстроки)) + СтрДлина(КлючПодстроки);
    Для Счетчик = НомерПервогоСимвола По СтрДлина(СтрокаЗапуска) Цикл
    Если Сред(СтрокаЗапуска, Счетчик, 1) = «;» Тогда
    НомерПоследнегоСимвола = Счетчик — 1;
    Прервать;
    КонецЕсли;
    КонецЦикла;
    
    // Получаем искомую подстроку
    ИскомаяПодстрока = Сред(СтрокаЗапуска, НомерПервогоСимвола, (НомерПоследнегоСимвола — НомерПервогоСимвола + 1));
    Если Сред(ИскомаяПодстрока, 1, 1) = «»»» Тогда
    // Если подстрока в кавычках, то выделяем их
    ИскомаяПодстрока = Сред(ИскомаяПодстрока, 2, (СтрДлина(ИскомаяПодстрока) — 2));
    КонецЕсли;
    
    Возврат ИскомаяПодстрока;
    
    КонецФункции // ВыделитьПодстроку()

    Показать

    Reply
  8. nikitin19819

    (7) igo1, И вам спасибо, я добавил. Но, правда, хотел чтобы код менялся в одном месте. Ну и ладно… Я считаю, что обработка строки — это довольно-таки серьезная штука и полезная. А вообще, Может быть и так, что наш с Вами получившийся код можно оптимизировать. Но в этом виде, думаю, пока подойдет для нетиповых конфиг.

    Reply
  9. JusteRU

    Супер! А нет ли возможности реализации подобного функционала без использования com-объекта, чтобы на linux-сервере использовать по регламентному заданию, например?

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

    Reply
  10. nikitin19819

    JusteRU(9), к сожалению, только чуть менее 2х лет в 1С… Как регламентное задание можно сделать. Пробовал на 2003м серваке. Не подошло, потому-что в фоновом режиме иногда выскакивало не в тему сообщение о наличии пользователя в сети. Надо еще немножко переработать для «фона». А насчет Линукса — Ведь ком объект я вызываю чтобы использовать агент-коннектор. Как в обход коннектора все «прокрутить» будет время-разберусь. Счас работу ищу 🙁

    Reply
  11. MadDAD

    (2) g26516, пиарнусь немного. Для 7.7 и для 8.x подойдет мое детище — http://infostart.ru/public/248792/.

    Reply
  12. nikitin19819

    (11) MadDAD, возьмем на заметку 🙂

    Reply
  13. nikitin19819

    хаха. А через Delphi такоя имтерестно реализовано…

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *