Delphi TidTCPServer and TidTCPClient carrying record

I need help understanding how to transfer a record through Indy TCP Server / Client. I have 2 programs in which I put the client on another server. On the client on the button, I add connect: Client - TIdTCPClient

Client.Connect(); 

And on the server side, I add a line to remind that the client is connected to the ServerConnect event

 Protocol.Lines.Add(TimeToStr(Time)+' connected '); 

To send data from the client, I have an entry that I want to send:

 Tmyrecord = record IPStr: string[15]; end; 

And I have a submit button:

 procedure Tform1.ButtonSendClick(Sender: TObject); var MIRec: Tmyrecord; msRecInfo: TMemoryStream; begin MIRec.IPStr := '172.0.0.1'; msRecInfo := TMemoryStream.Create; msRecInfo.Write(MIRec, SizeOf(MIRec)); msRecInfo.Position := 0; Client.IOHandler.Write(msRecInfo); end; 

On the server side, onexecute I have the following code, I have the same tmyrecord, which is also indicated on the server side:

  procedure TServerFrmMain.ServerExecute(AContext: TIdContext); var MIRec: Tmyrecord; msRecInfo: TMemoryStream; begin if AContext.Connection.Connected then begin AContext.Connection.IOHandler.CheckForDataOnSource(10); if not AContext.Connection.IOHandler.InputBufferIsEmpty then begin msRecInfo:= TMemoryStream.Create; AContext.Connection.IOHandler.ReadStream(msRecInfo); msRecInfo.Read(MIRec, sizeOf(msRecInfo)); ShowMessage(MIRec.IPStr); end; end; end 

I do not know why it does not work, why I can not show the IP address that I wrote from the client side. I want to read a record (msRecInfo) on the server side, which I send from the client side. I want to access my recording items, in which case I want to read the IPSTR element of my recording. When I click the submit button on the client side, the application freezes, the server side.

Thank you very much in advance

+6
source share
1 answer

You are making a classic novice mistake - you expect that the default behavior of the TIdIOHandler.Write(TStream) and TIdIOHandler.ReadStream() methods will match, but they donโ€™t actually do this.

The default values โ€‹โ€‹of TIdIOHandler.ReadStream() indicate that he should expect Integer or Int64 (depending on the value of the TIdIOHandler.LargeStream property) to chase stream data to indicate the length of the data.

However, the default parameter values TIdIOHandler.Write(TStream) do not tell him to send such an Integer/Int64 value. So your use of TIdIOHandler.ReadStream() reads the first few bytes of the record and interprets them as Integer/Int64 (which is 926036233 given the string value you are sending) and then waits for so many bytes to arrive that there will never be a TIdIOHandler.ReadStream() does not exit (unless you set the TIdIOHandler.ReadTimeout property to an infinite value).

There are other minor bugs / typos in the code that use TMemoryStream objects outside of Indy.

Try this instead:

 procedure Tform1.ButtonSendClick(Sender: TObject); var MIRec: Tmyrecord; msRecInfo: TMemoryStream; begin MIRec.IPStr := '172.0.0.1'; msRecInfo := TMemoryStream.Create; try msRecInfo.Write(MIRec, SizeOf(MIRec)); // writes the stream size then writes the stream data Client.IOHandler.Write(msRecInfo, 0, True); finally msRecInfo.Free; end; end; procedure TServerFrmMain.ServerExecute(AContext: TIdContext); var MIRec: Tmyrecord; msRecInfo: TMemoryStream; begin msRecInfo := TMemoryStream.Create; try // reads the stream size then reads the stream data AContext.Connection.IOHandler.ReadStream(msRecInfo, -1, False); msRecInfo.Position := 0; msRecInfo.Read(MIRec, SizeOf(MIRec)); ... finally msRecInfo.Free; end; end; 

Or that:

 procedure Tform1.ButtonSendClick(Sender: TObject); var MIRec: Tmyrecord; msRecInfo: TMemoryStream; begin MIRec.IPStr := '172.0.0.1'; msRecInfo := TMemoryStream.Create; try msRecInfo.Write(MIRec, SizeOf(MIRec)); // does not write the stream size, just the stream data Client.IOHandler.Write(msRecInfo, 0, False); finally msRecInfo.Free; end; end; procedure TServerFrmMain.ServerExecute(AContext: TIdContext); var MIRec: Tmyrecord; msRecInfo: TMemoryStream; begin msRecInfo := TMemoryStream.Create; try // does not read the stream size, just the stream data AContext.Connection.IOHandler.ReadStream(msRecInfo, SizeOf(MIRec), False); msRecInfo.Position := 0; msRecInfo.Read(MIRec, SizeOf(MIRec)); ... finally msRecInfo.Free; end; end; 

Alternatively, you can send a record using TIdBytes instead of TStream :

 procedure Tform1.ButtonSendClick(Sender: TObject); var MIRec: Tmyrecord; Buffer: TIdBytes; begin MIRec.IPStr := '172.0.0.1'; Buffer := RawToBytes(MIRec, SizeOf(MIRec)); Client.IOHandler.Write(Buffer); end; procedure TServerFrmMain.ServerExecute(AContext: TIdContext); var MIRec: Tmyrecord; Buffer: TIdBytes; begin AContext.Connection.IOHandler.ReadBytes(Buffer, SizeOf(MIRec)); BytesToRaw(Buffer, MIRec, SizeOf(MIRec)); ... end; 
+10
source

Source: https://habr.com/ru/post/917320/


All Articles