Indy 10 problems with reading and writing streams - stream

I am trying to exchange data between a IdTCPServer and an IdTCPClient using a collection that I convert to a stream before I send it over the network. Unfortunately, no matter how I try, I can't seem to pass the stream between the client and the server. The code always hangs on the IdTCPClient1.IOHandler.ReadStream(myStream, -1, True) line.
The relevant portion of my code is shown below:
Client side
with ClientDataModule do
begin
try
try
intStreamSize := StrToInt(IdTCPClient1.IOHandler.ReadLn); // Read stream size
IdTCPClient1.IOHandler.ReadStream(myStream, -1, True); // Read record stream
finally
ReadCollectionFromStream(TCustomer, myStream);
end;
except
ShowMessage('Unable to read the record from stream');
end;
end;
Server side
try
try
SaveCollectionToStream(ACustomer, MStream);
finally
MStream.Seek(0, soFromBeginning);
IOHandler.WriteLn(IntToStr(MStream.Size)); // Send stream size
IOHandler.Write(MStream, 0); // Send record stream
end;
except
ShowMessage('Unable to save the record to stream');
end;
I would greatly appreciate your assistance with solving this problem.
Thanks,
JDaniel

You are setting the AReadUntilDisconnect parameter of ReadStream() to True, which tells it to keep reading until the connection is closed. You need to set the parameter to False instead. You also need to pass in the stream size in the AByteCount parameter, since you are sending the stream size separately so you have to tell ReadStream() how much to actually read.
Try this:
Client:
with ClientDataModule do
begin
try
intStreamSize := StrToInt(IdTCPClient1.IOHandler.ReadLn);
IdTCPClient1.IOHandler.ReadStream(myStream, intStreamSize, False);
myStream.Position := 0;
ReadCollectionFromStream(TCustomer, myStream);
except
ShowMessage('Unable to read the record from stream');
end;
end;
Server:
try
SaveCollectionToStream(ACustomer, MStream);
MStream.Position := 0;
IOHandler.WriteLn(IntToStr(MStream.Size));
IOHandler.Write(MStream);
except
ShowMessage('Unable to save the record to stream');
end;
If you can change your protocol, then you can let Write() and ReadStream() exchange the stream size internally for you, like this:
Client:
with ClientDataModule do
begin
try
// set to True to receive a 64bit stream size
// set to False to receive a 32bit stream stream
IdTCPClient1.IOHandler.LargeStream := ...;
IdTCPClient1.IOHandler.ReadStream(myStream, -1, True);
myStream.Position := 0;
ReadCollectionFromStream(TCustomer, myStream);
except
ShowMessage('Unable to read the record from stream');
end;
end;
Server:
try
SaveCollectionToStream(ACustomer, MStream);
MStream.Position := 0;
// set to True to send a 64bit stream size
// set to False to send a 32bit stream stream
IOHandler.LargeStream := ...;
IOHandler.Write(MStream, 0, True);
except
ShowMessage('Unable to save the record to stream');
end;

Related

How to cancel a tcpclient stream on Delphi and Indy 10.6

I'm setting up a TCP client/server app, where I send a header record to the server (and possibly a file stream), and it will respond with a response record (and possibly a file stream).
But some times, I get stream errors (both sides), and I can't seem to clear them with IOHandler.InputBuffer.Clear().
Here's some example code:
CLIENT SIDE
var
FS: TFileStream;
FClient.IOHandler.LargeStream := True;
FS := TFileStream.Create(FileName, fmOpenRead);
try
FClient.IOHandler.Write(FS, FS.Size, True); //send the file w/size
finally
FreeAndNil(FS);
end;
SERVER SIDE
var
FS: TFileStream;
//in ServerExecute
FS := TFileStream.Create(FileName, fmCreate Or fmOpenWrite); //create the file
IdContext.Connection.IOHandler.LargeStream := True;
try
if IdContext.Connection.IOHandler.InputBufferIsEmpty then //if no data yet
if not IdContext.Connection.IOHandler.CheckForDataOnSource(30000) then
begin //read timeout
SErr := 'read time out, stream not received!';
Exit;
end;
IdContext.Connection.IOHandler.ReadStream(FS, -1, False); //read stream with size specified at start of stream
if FS.Size <> (expected file size sent in header packet) then
begin
SErr := 'not all stream bytes received!';
Exit;
end;
finally
FreeAndNil(FS); //close the file
end;
Regardless of whether it is the server or client side receiving, when I do IdContext.Connection.IOHandler.ReadStream(FS, -1, False) it will freeze on the stream intermittently (probably because the transfer count is less than the expected bytes). Once I detect a timeout condition, I will clear the input buffer, but from then on it's like the transfers are out of sync; i.e. I send a command packet, then the file packet, but I get an error like it received the file packet first, and it keeps doing that for the following failure retries.

INDY10 Stream write error while I trying to send data trought FTP

I want to put data from Memo1 directly to my FTP server, I've got code:
procedure TForm5.SendClick(Sender: TObject);
var K: TStream;
begin
K := TStream.Create;
Memo1.Lines.SaveToStream(K);
FTP.Host := 'localhost';
FTP.Username := 'login';
FTP.Password := 'haslo';
FTP.Connect;
if FTP.Connected then FTP.Put(K,'');
end;
But when I click "Send" button I've got two errors:
when Memo is empty
when I try send data
TStream is an abstract class. You must never instantiate it. Use a concrete class instead like, for instance, TMemoryStream.
You'll also want to destroy the stream when you are finished with it, or it will leak. Do yourself a favour and set ReportMemoryLeaksOnShutdown to True, for instance in your .dpr file. That will allow you to get a report of all the memory you are leaking when your program terminates.
Your code might run like this:
var
Stream: TMemoryStream;
....
Stream := TMemoryStream.Create;
try
// .... initialize the Indy object
if FTP.Connected then begin
// .... populate stream
Stream.Position := 0;
FTP.Put(Stream, '');
end;
finally
Stream.Free;
end;

Memo1.Lines.LoadFromStream from IOHandler.ReadStream

I was trying to display the sent textfile to memo.lines with out saving it to a disk
from server
try
Ms := TMemoryStream.Create;
Ms.LoadFromFile('update.txt');
Ms.Position := 0;
AContext.Connection.IOHandler.LargeStream := True;
AContext.Connection.IOHandler.Write(Ms, 0, True);
finally
Ms.Free;
end;
to client...im not sure how to do this in client
try
Ms := TMemoryStream.Create;
Ms.Position := 0;
IdTCPClient1.IOHandler.LargeStream := True;
IdTCPClient1.Connection.IOHandler.ReadStream(Ms, -1,false);
finally
Memo1.Lines.LoadFromStream(Ms);
Ms.Free;
end;
can anyone help me on how to make this work if its possible ?
Your code is fine, you simply forgot to reset the stream's Position property back to 0 before calling the Memo's LoadFromStream() method:
IdTCPClient1.Connection.IOHandler.ReadStream(Ms, -1,false);
Ms.Position := 0; // <-- add this
Memo1.Lines.LoadFromStream(Ms);
If I were you, I would still buffer the read data because of two reasons:
The network stream is unidirectional and non-positionable therefore not really file like. (Yes, it also means, that you must refresh it manually/programmatically every time some new data is received.)
If the connection fails the read buffer could be filled with gibberish which gets diplayed, because You have no further means to control what gets displayed once you redirected the input of your memo.
If you do not want to save the sent data to disk, you could still save it to a TMemoryStream instance, which could be used as a parameter of the memo's .LoadFromStream() method.

Send Files with TCP from client to server knowing file size Delphi

Hello im trying to send a file Via TCP with delphi using Indy Components with a Client/Server
i manage to send and recieve the files correctly, the problem is before sending the file i would like to send its size aswell to compare it after i get it the server.
Now im sending the files from the Client To Server.
Client:
Ms := TMemoryStream.Create;
Ms.LoadFromFile('cliente.exe');
Ms.Position := 0;
Result := True;
Client.IOHandler.LargeStream := True;
try
Client.IOHandler.Write(ms, 0, True);// (Ms, 0, true);
except
Result := False;
end;
Ms.Free;
Server:
AStream := TFileStream.Create('C:\temp\file.exe', fmCreate + fmShareDenyNone);
try
AContext.Connection.IOHandler.LargeStream := True;
AContext.Connection.IOHandler.ReadStream(AStream, -1,false);
finally
FreeAndNil(AStream);
Memo1.Lines.Add('File received');
end;
So the question would be how could i send the file size with the file?
Your code is already sending the file size. You are setting the AWriteByteCount parameter of Write(TStream) to True, which tells it to send the stream size before sending the stream data. And you are telling ReadStream() to read the stream size before reading the stream data. So Indy is already validating the size for you before ReadStream() exits. You don't need to do it manually at all.

Delphi Indy equivalent to Python's Socket.recv?

I've this code snippet in Python:
s = socket.create_connection(('192.168.0.111', 123), timeout=2.0)
s.sendall('REQUEST,status,interface'); result = s.recv(1024)
How I can do the "s.recv(1024)" in Delphi using TIdTCPClient from Indy components? Server returns a string without any terminator, so ReadLn doesn't work.
In Python, recv(1024) simply reads whatever is available on the socket, up to 1024 bytes max. It is possible to do the same thing with TIdTCPClient (see below), but it is not the best way to handle socket programming in general. You really need to know how the server is actually terminating the data. Does it close its end of the connection after sending the data? If not, does it expect you to simply read whatever bytes are available regardless of the actual length? And if so, how does it expect you to handle TCP packet fragmentation (TCP/IP can separate transmitted data into multiple packets, and may receive data as multiple packets even if they were not sent as such)? Does it expect you to keep reading until some timeout occurs, indicating no more data is being sent?
It makes a big difference in how you write code to handle the reading properly. For example:
IdTCPClient1.Host := '192.168.0.111';
IdTCPClient1.Port := 123;
IdTCPClient1.ConnectTimeout := 2000;
IdTCPClient1.Connect;
try
IdTCPClient1.IOHandler.Write('REQUEST, status, interface');
// wait for a disconnect then return everything that was received
Result := IdTCPClient1.IOHandler.AllData;
finally
IdTCPClient1.Disconnect;
end;
Vs:
IdTCPClient1.Host := '192.168.0.111';
IdTCPClient1.Port := 123;
IdTCPClient1.ConnectTimeout := 2000;
IdTCPClient1.Connect;
try
IdTCPClient1.IOHandler.Write('REQUEST, status, interface');
// set the max number of bytes to read at one time
IdTCPClient1.IOHandler.RecvBufferSize := 1024;
// read whatever is currently in the socket's receive buffer, up to RecvBufferSize number of bytes
IdTCPClient1.IOHandler.CheckForDataOnSource;
// return whatever was actually read
Result := IdTCPClient1.IOHandler.InputBufferAsString;
finally
IdTCPClient1.Disconnect;
end;
Vs:
IdTCPClient1.Host := '192.168.0.111';
IdTCPClient1.Port := 123;
IdTCPClient1.ConnectTimeout := 2000;
IdTCPClient1.Connect;
try
IdTCPClient1.IOHandler.Write('REQUEST, status, interface');
// keep reading until 5s of idleness elapses
repeat until not IdTCPClient1.IOHandler.CheckForDataOnSource(5000);
// return whatever was actually read
Result := IdTCPClient1.IOHandler.InputBufferAsString;
finally
IdTCPClient1.Disconnect;
end;

Resources