Как отображать сообщения в каждом событии Indy?

Я работаю над приложением C++ Builder, используя библиотеку Indy. Есть две основные цели:

  1. Для выполнения переадресации между сервером и клиентом.
  2. Чтобы визуализировать трафик и его декодирование в окне (не консоль, приложение Windows)

После реализации решения, описанного в:

Почему мне нужно отправить сообщение дважды, чтобы вызвать событие Indy OnExecute?

И доработав решение под свои нужды, приложение начало зависать. Судя по всему, приложение работает хорошо, пока я не захочу остановить компонент TIdMappedPortTCP или закрыть приложение.

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

Я смог воспроизвести нежелательное поведение, просто «распечатав» сообщения во время событий Инди.

Событие OnExecute еще не реализовано (в новом проекте), что означает, что данные TCP передаются в обе стороны.

Это моя реализация класса синхронизации:

// TTextToDisplay.cpp
TTextToDisplay::TTextToDisplay() {
    lineToAdd = NULL;
    stringsToAdd = NULL;
}

TTextToDisplay::TTextToDisplay(String str) {
    lineToAdd = str;
    stringsToAdd = NULL;
}

void __fastcall TTextToDisplay::AddSingleLine(String str)
{
    lineToAdd = str;
}

void __fastcall TTextToDisplay::AddStringList(TStringList* strings)
{
    stringsToAdd = strings;
}

void __fastcall TTextToDisplay::DoSynchronize(){
            // Use the input parameters here...
        if (stringsToAdd)
            Form1->Display->Lines->AddStrings(stringsToAdd);
        else if (lineToAdd != NULL) {
            Form1->Display->Lines->Add(lineToAdd);
        }
}

Где Display — это элемент управления TRichEdit, lineToAdd — это объект String, а stringsToAdd — это TStringList.

Indy управляет несколькими событиями, и я хотел бы добавить текст на каждое из них (если это не запрещено дизайном или каким-либо другим ограничением).

Я добавил это событие:

void __fastcall TForm1::MITMProxyBeforeConnect(TIdContext *AContext)
{
    String tempStr;

    // displaying remote address.
    tempStr = "Received connection from " +
        AContext->Connection->Socket->Binding->PeerIP;
    TTextToDisplay *TextToDisplay = new TTextToDisplay(tempStr);
    TextToDisplay->Synchronize();
    delete TextToDisplay;
}

Приложение работало нормально. Затем я добавил это:

void __fastcall TForm1::MITMProxyDisconnect(TIdContext *AContext)
{
    String tempStr;

    // displaying remote address.
    tempStr = "Client disconnected"; TTextToDisplay *TextToDisplay =
        new TTextToDisplay(tempStr); TextToDisplay->Synchronize();
    delete TextToDisplay;
}

Приложение по-прежнему работает нормально. Поэтому я добавил еще один

void __fastcall TForm1::MITMProxyConnect(TIdContext *AContext)
{
    String tempStr;

    // displaying remote address.
    tempStr = "Attempting to connect to the remote server " +
        MITMProxy->MappedHost + ":" + MITMProxy->MappedPort;
    TTextToDisplay *TextToDisplay = new TTextToDisplay(tempStr);
    TextToDisplay->Synchronize();
    delete TextToDisplay;

}

И теперь приложение начинает зависать.

Работает нормально означает, что я могу закрыть TIdMappedPortTCP

MITMProxy->Active = False;

и повторно активировать его

MITMProxy->Active = True;

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

Я хотел бы, чтобы приложение было как можно более подробным, так есть ли лучший способ регистрировать каждое событие Indy?

Приложение, над которым я изначально работал, работало отлично, пока я не добавил настраиваемое отображение данных. Потом появилась проблема. Я не знаю, связана ли причина, но поведение такое же.

Итак, мое приложение вместо отображения этой строки:

008460000000190210703800000EC00000164593560001791662000000000000080000000002104302040235313531353135313531353153414C4535313030313233343536373831323334353637383930313233

Теперь показывает:

0000: 60 00 00 00 19 02 10 70 38 00 00 0E C0 00 00 16 
0010: 45 93 56 00 01 79 16 62 00 00 00 00 00 00 08 00 
0020: 00 00 00 02 10 43 02 04 02 35 31 35 31 35 31 35 
0030: 31 35 31 35 31 53 41 4C 45 35 31 30 30 31 32 33 
0040: 34 35 36 37 38 31 32 33 34 35 36 37 38 39 30 31 
0050: 32 33 

Это TStringList, потому что мне проще сразу добавить все эти строки в TRichEdit. Я хочу, чтобы второй TRichEdit показывал декодированное сообщение.

Чтобы дать вам представление, это будет что-то вроде этого:

000 MsgType              : "0200"
001 BitMap               : "70 24 06 80 20 C0 06 10"
002 PAN                  : "4593560001791662"
003 ProcessingCode       : "000000"
004 TxnAmount            : "000000080000"
011 SystemTraceNo        : "000001"
014 ExpirationDate       : "2411"
022 POSEntryMode         : "520"
023 CardSequenceNo       : "000"
025 POSConditionCode     : "00"
035 Track2               : "4593560001791662=24111190000063900000"
041 TerminalID           : "00064600"
042 AcquirerID           : "000010585800001"
054 AddAmounts           : "0"
055 Field55              : "9F 26 08 35 C2 C4 DF B5 FC 7B 0E 9F 27 01 80 9F 10 07 06 01 0A 03 A0 B8 03 9F 37 04 C1 5C 4B 3B 9F 36 02 01 3A 95 05 00 80 00 80 00 9A 03 22 04 02 9C 01 00 9F 02"
060 Field60              : "00 00 08"
--------------------------------------------------------------
Field 55 by Tag:
9F26 AppCryptogram        : "35 C2 C4 DF B5 FC 7B 0E"
9F27 CryptogramInfoData   : "80"
9F10 IssuerAppData        : "06 01 0A 03 A0 B8 03"
9F37 UnpredictableNo      : "C1 5C 4B 3B"
9F36 AppTxnCounter        : "01 3A"
95   TermVerifResults     : "00 80 00 80 00"
9A   TxnDate              : "220402"
9C   TxnType              : "00"
9F02 AmountAuthNum        : "20"

И последнее соображение: я знаю приложения, которые выполняют пересылку, и я знаю приложения, которые декодируют, но я не видел приложений, которые делают и то, и другое, и поэтому я хочу создать его. Но до сих пор самой сложной частью была визуализация. Поэтому я не знаю, использую ли я правильные инструменты или правильный подход для создания того, что хочу. Любые советы будут высоко оценены.

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
51
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Серверы Indy являются многопоточными, а отключение сервера является синхронным — он ожидает полного завершения потоков сервера, прежде чем вернуться к вызывающей стороне.

TIdSyncTThread::Synchronize() и эквивалентные) делегируют основному потоку пользовательского интерфейса, блокируя вызывающий поток до тех пор, пока основной поток не завершит обработку запроса на синхронизацию.

Таким образом, вы можете легко попасть в тупиковую ситуацию, если вы Synchronize с основным потоком, в то время как основной поток выключает сервер. Основной поток будет ожидать в потоках сервера, а поток синхронизации будет ожидать в основном потоке.

Итак, ваши варианты: либо:

  • Просто не Synchronize во время отключения сервера. Установите флаг где-нибудь перед началом выключения, а затем посмотрите на этот флаг перед выполнением синхронизации. Хотя это не будет охватывать сценарий, когда отключение начинается после того, как синхронизация уже запущена.

  • Не выполняйте синхронную синхронизацию, вместо этого выполняйте асинхронную синхронизацию (TIdNotify, TThread::Queue() или эквивалент). Нет веской причины блокировать потоки вашего сервера, ожидающие, пока основной поток выполнит обновления состояния пользовательского интерфейса.

  • Завершите работу сервера в отдельном потоке, оставив основной поток свободным для обработки запросов синхронизации.

Большое спасибо @Remy После комментирования кода в моем событии OnDisconnect отзывчивость вернулась. Я понимаю, что сейчас происходило. Выключение сервера вызывает событие OnDisconnect, которое пытается выполнить синхронизацию, создавая ситуацию взаимоблокировки. Я скучаю по тексту «клиент отключен», но это можно решить позже. Мне нужно изучить два других решения. Извините за длинный вопрос.

Beto 21.12.2022 19:42

Другие вопросы по теме