Чтение пакетов с разделителями с помощью Indy TIdTCPServer в C Builder

#c builder #indy

#c builder #indy

Вопрос:

У меня есть микропроцессорное устройство, которое хочет подключиться к TCP-серверу, а затем отправлять токен каждый раз, когда изменяется его входное состояние. Токены представлены в форме "AS9=0;" без CR или LF . Каждый токен отправляется в одном пакете, идентифицированном длиной 6. Чтобы мой проект работал должным образом, я хочу разрешить соединению открываться, получать данные и использовать их, не дожидаясь другой упаковки или закрытия соединения.

Набор компонентов сокетов, поставляемый с C Builder, — это Indy 10. Это представляет собой поразительный объем работы. Однако вся эта работа написана на Паскале, мне это не очень полезно. Было бы великодушно сказать, что документация сложна. В идеале я хотел бы считывать данные по пакету за пакетом. Я не могу определить средства для этого. Я пробовал этот AllData() метод. Хотя это работает, оно ожидает, пока соединение не будет закрыто, чтобы считывать данные. Это не подходит для меня. Я также пробовал этот ReadLn() метод. Он считывает данные, как только встречает a LF . Это тоже бесполезно, так как мои данные не содержат LF s.

Пока мой код выглядит так:

 void __fastcall TForm1::IdTCPServer1Execute(TIdContext *AContext)
{
    UnicodeString mystring;
    RichEdit1->Lines->Add(" * ");
    mystring = AContext->Connection->IOHandler->AllData();
    RichEdit1->Lines->Add(mystring   " *** ");
}
//-----------------------------------
void __fastcall TForm1::IdTCPServer1Disconnect(TIdContext *AContext)
{
    RichEdit1->Lines->Add("Disconnected. . .  rn");
}
//--------------------------------------
void __fastcall TForm1::IdTCPServer1Connect(TIdContext *AContext)
{
    RichEdit1->Lines->Add("Connected. . .  rn");
}
//---------------------------------------
 

Как я уже упоминал, приведенный выше код покажет все, но только после закрытия соединения. Учитывая все это, у меня есть два вопроса. Во-первых, как я могу получить данные сразу после поступления пакета? ReadLn() Во-вторых, как я могу изменить разделитель строк с a LF на a ";" ?

Ответ №1:

Если вы посмотрите на объявление TIdIOHandler::ReadLn() , вы увидите, что оно имеет необязательный ATerminator параметр:

 String __fastcall ReadLn(_di_IIdTextEncoding AByteEncoding  = NULL);

String __fastcall ReadLn(String ATerminator, _di_IIdTextEncoding AByteEncoding);

String __fastcall ReadLn(String ATerminator, int ATimeout = IdTimeoutDefault, int AMaxLineLength = -1, _di_IIdTextEncoding AByteEncoding = NULL);
 

Если вы не укажете ограничитель, по умолчанию используется LF (который включает CRLF ). Вы можете указать другой терминатор по желанию, например:

 void __fastcall TForm1::IdTCPServer1Execute(TIdContext *AContext)
{
    ...
    String mystring = AContext->Connection->IOHandler->ReadLn(_D(";"));
    ...
}
 

Отдельно отметим, TIdTCPServer что это многопоточный компонент. Его события запускаются в контексте рабочих потоков. Таким образом, вы ДОЛЖНЫ синхронизироваться с основным потоком пользовательского интерфейса при доступе к элементам управления пользовательского интерфейса, например:

 void __fastcall TForm1::AddLine(const String amp;S)
{
    #if defined(__clang__)
    TThread::Synchronize(nullptr, [amp;, this](){ RichEdit1->Lines->Add(S); });
    #else
    struct TAddLineSync
    {
        String Line;
        TAddLineSync(const String amp;ALine) : Line(ALine) {}
        void __fastcall AddLine() { Form1->RichEdit1->Lines->Add(Line); }
    };
    TAddLineSync sync(S);
    TThread::Synchronize(NULL, amp;sync.AddLine);
    #endif
}
//-----------------------------------
void __fastcall TForm1::IdTCPServer1Execute(TIdContext *AContext)
{
    AddLine(_D(" * "));
    String mystring = AContext->Connection->IOHandler->ReadLn(_D(";"));
    AddLine(mystring   _D(" *** "));
}
//-----------------------------------
void __fastcall TForm1::IdTCPServer1Disconnect(TIdContext *AContext)
{
    AddLine(_D("Disconnected. . .  rn"));
}
//--------------------------------------
void __fastcall TForm1::IdTCPServer1Connect(TIdContext *AContext)
{
    AddLine(_D("Connected. . .  rn"));
}
//---------------------------------------