#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"));
}
//---------------------------------------