Indy Client Daten Empfangen
-
Hallo zusammen,
ich hab bis jetzt meine TCP Kommunikation über TServerSocket und TClientSocket aufgebaut. Da aber sehr oft Indy angepriesen wird, bin ich gerade dabei mich damit auseinander zu setzten.Der Aufbau der Kommunikation und der Datenaustausch funktioniert soweit. Mich macht nur der Empfang der Daten beim Client etwas stutzig. Gibt es kein Event für den Datenempfang? beim TClientSocket und TServerSocket gibt es die schöne Funktion OnRead. Beim Indy Server gibt die OnExecute die Möglichkeit des Datenempfangs aber für den Client finde ich nichts. Muss ich dafür einen eigenen Thread erstellen oder hab ich was übersehen?
Danke schonmal
-
Beim Indy TCP Server nutze ich den Thread der ohnehin mit der Komponente läuft:
void __fastcall TForm1::Server1Execute(TIdPeerThread *AThread) { char Buff[1024]; AThread->Connection->ReadBuffer(Buff,1024); .... ..........
Beim Client lasse ich einen Timer im 10ms Intervall laufen (alternativ kann man natürlich auch einen Thread nutzen):
if(Client1->Connected() && Client1->Socket->Readable(0) && Client1->ClosedGracefully == false) { char Buff[1024]; Client1->ReadBuffer(Buff,1024); ..... ......... ............
OnConnect started den Timer, OnDisconnect beendet ihn.
-
Also mit dem Empfang der Daten auf der Clientseite gibt es bei mir noch Probleme. Woher weiß der Client das Daten vorhanden sind?
Client1->Socket->Readable(0) gibt immer false zurück. Egal ob Daten da sind oder nicht.
Wenn ich Daten lese und keine vorhanden sind, hängt sich das Programm auf.
Wenn ich ReadTimeout auf z.B. 10 ms setze, und Daten lese wenn keine da sind, gibt es ein Exception. Den kann ich zwar auffangen, aber das kann doch nicht der richtige weg sein, oder?
Hab das ganze übrigens auch anstelle mit Socket mit IOHandler getestet. ist aber kein Unterschied zu erkennen.
Gibt es nicht ein vernünftiges tutorial zu Indy? Wenn man bei google Beispiele findet sind die meistens in Delphi und nicht in c++. 90% der tutorials erklären wie man mit WriteLn einen Text überträgt. Ich würde aber gerne Daten/ ganze structuren übertragen.
-
Nach etwas längerem suchen hab ich eine Möglichkeit gefunden.
if(IVClient->Connected()&& IVClient->Socket->ClosedGracefully==false) { IVClient->Socket->CheckForDataOnSource(10); if(!IVClient->Socket->InputBufferIsEmpty()) { // Datenempfang } }
-
Komisch, bei mir funktioniert die Methodik seit Jahren hervorragend. Pollst du mit nem Timer oder mit einem Thread? Readable(0) gibt bei mir true zurück wenn das Socket für den Lesevorgang bereit ist. Die Nutzung von IdAntiFreeze ist in den meisten Fällen nötig wenn man Readable(0) verwendet. ReadTimeout ist bei mir 0. Deine Methodik gefällt mir wesentlich besser und werde damit meinen Quelltext verbessern. Der schnöde Timer zum Pollen würde ich gerne verwerfen und einen Thread zum Lesen initialisieren, aber die VCL ist ja nicht unbedingt threadsafe...daher diese Lösung bisher. Datenstrukturen als Packet verwende ich so:
Senden:
struct UAS { char Id [4]; //UAS char User [255]; char Password [255]; }; UAS send_pack; memset(&send_pack, 0, sizeof(UAS)); strcpy(send_pack.Id,"UAS"); strcpy(send_pack.User,LabeledEdit2->Text.c_str()); strcpy(send_pack.Password,LabeledEdit3->Text.c_str()); if(Client1->Connected()) Client1->WriteBuffer((char *)&send_pack,1024,true);
Empfangen:
struct StatePack { char Id[4]; // MSO + zero float ReplaySpeed; WORD Flags; byte InGameCam; byte ViewPlayer; byte NumPlayers; byte NumConns; byte NumFinished; byte RaceInProgress; byte QualMins; byte RaceLaps; byte Spare2; byte Spare3; char Track[6]; byte Weather; byte Wind; }; void __fastcall TForm1::ReceiveTimer1Timer(TObject *Sender) { if(Client1->Connected() && Client1->Socket->Readable(0) && Client1->ClosedGracefully == false) { char Buff[1024]; Client1->ReadBuffer(Buff,1024); if(strcmp(Buff,"MSO") == 0) { StatePack *pack = (StatePack *)Buff; String clearstring = GetClearString(String(pack->Track)); ..... .........
-
Ich nutze Indy 10 da hat sich leider einiges geändert. z.B. WriteBuffer gibt es da nicht mehr. Ich schmeiß die daten derzeit in ein TDynByteArray zum versenden. Funktioniert eigenlich ganz gut.
TByteDynArray Bytes; Bytes.Length=sizeof(IPMESSAGE); memcpy(&Bytes[0],&Message,sizeof(IPMESSAGE)); IVClient->Socket->Write(Bytes);
IPMESSAGE ist meine zu sendende structur
für Threads hab ich mir mal eine eigene Klasse geschrieben die das Handling der Threads für mich vereinfacht.
-
Ich benutze noch die Indy 9 mit BCB6 aber WriteBuffer() wurde wohl durch Write() ersetzt wie dein Beispiel zeigt. Ich werde mir nächsten Monat den CBuilder XE3 Professional kaufen und bin mal gespannt ob sich meine alten Projekte kompilieren lassen. Denke aber das die ein oder andere alte Komponente zu Problemen führen wird.