Reputation: 2374
I'm sorry I know that I ask too many questions, but tomorrow (actually today cause in my coutry it's 2:00 am right now) I need to show to my teacher what I started to make e.t.c. So as I asked in previous questions I need to send from server to client some data.
But it nothing appers in server's memo field after thatt memo1.Lines.Add(IntToStr(arrOf[1]));
I was trying to send it like that on client
procedure TForm1.btnTestClick(Sender: TObject);
var
msRecInfo: TMemoryStream;
arrOf: array of integer; i:integer;
begin
setLength(arrOf, 11);
for i := 0 to 10 do
arrOf[i]:=random(100);
msRecInfo:= TMemoryStream.Create;
try
msRecInfo.Write(arrOf, SizeOf(arrOf));
idTCPClient1.IOHandler.Write(msRecInfo);
finally
msRecInfo.Free;
end;
end;
on server
procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);
var
msRecInfo: TMemoryStream;
arrOf: array of Integer; i:integer;
begin
msRecInfo:= TMemoryStream.Create;
try
AContext.Connection.IOHandler.ReadStream(msRecInfo, -1, False);
SetLength(arrOf,11);
msRecInfo.Position := 0;
msRecInfo.Read(arrOf, SizeOf(arrof));
finally
memo1.Lines.Add(IntToStr(arrOf[1]));
msRecInfo.Free;
end;
end;
Please could you help me to solve this problem and to find some examples of how to send arrays of different types/classes?
Upvotes: 0
Views: 1428
Reputation: 598134
As Rufo already explained, you are not writing the array into, and reading the array out of, the TMemoryStream
correctly.
Worse, you are not sending the TMemoryStream
over the socket correctly, either. The default parameters of TIdIOHandler.Write(TStream)
and TIdIOHandler.ReadStream()
are not compatible with each other. By default, Write(TStream)
does not send the TStream.Size
value. However, the default parameters of ReadStream()
(which are the same values that you are passing in explicitally) tell it to read the first few bytes and interpret them as the Size
, which would be very wrong in this example.
Try this instead:
procedure TForm1.btnTestClick(Sender: TObject);
var
msRecInfo: TMemoryStream;
arrOf: Array of Integer;
i: Integer;
begin
SetLength(arrOf, 11);
for i := Low(arrOf) to High(arrOf) do
arrOf[i] := random(100);
msRecInfo := TMemoryStream.Create;
try
msRecInfo.WriteBuffer(arrOf[0], Length(arrOf) * SizeOf(Integer));
IdTCPClient1.IOHandler.Write(msRecInfo, 0, True);
finally
msRecInfo.Free;
end;
end;
procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);
var
msRecInfo: TMemoryStream;
arrOf: Array of Integer;
i: Integer;
begin
msRecInfo := TMemoryStream.Create;
try
AContext.Connection.IOHandler.ReadStream(msRecInfo, -1, False);
SetLength(arrOf, msRecInfo.Size div SizeOf(Integer));
if Lenth(arrOf) > 0 then
begin
msRecInfo.Position := 0;
msRecInfo.ReadBuffer(arrOf[0], Length(arrOf) * SizeOf(Integer));
end;
finally
msRecInfo.Free;
end;
...
end;
Alternatively, get rid of the TMemoryStream
and send the individual Integer
values by themselves:
procedure TForm1.btnTestClick(Sender: TObject);
var
arrOf: Array of Integer;
i: Integer;
begin
SetLength(arrOf, 11);
for i := Low(arrOf) to High(arrOf) do
arrOf[i] := random(100);
IdTCPClient1.IOHandler.Write(Length(arrOf));
for I := Low(arrOf) to High(arrOf) do
IdTCPClient1.IOHandler.Write(arrOf[i]);
end;
procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);
var
arrOf: Array of Integer;
i: Integer;
begin
i := AContext.Connection.IOHandler.ReadLongInt;
SetLength(arrOf, i);
for i := Low(arrOf) to High(arrOf) do
arrOf[i] := AContext.Connection.IOHandler.ReadLongInt;
...
end;
Now, with that said, accessing the TMemo
directly in the OnExecute
event handler is not thread-safe. TIdTCPServer
is a multi-threaded component. The OnExecute
event is triggered in the context of a worker thread, not the main thread. UI components, like TMemo
, cannot be safely accessed from outside of the main thread. You can use Indy's TIdNotify
or TIdSync
class to synchronize with the main thread, eg:
type
TMemoSync = class(TIdSync)
protected
FLine: String;
procedure DoSynchronize; override;
end;
procedure TMemoSync.DoSynchronize;
begin
Form1.Memo1.Lines.Add(FLine);
end;
procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);
var
...
begin
...
with TMemoSync.Create do try
FLine := IntToStr(arrOf[1]);
Synchronize;
finally
Free;
end;
...
end;
If you do not synchronize with the main thread, bad things can happen.
Upvotes: 3
Reputation: 19106
This is your Main Problem here
msRecInfo.Write( arrOf, SizeOf( arrOf ) );
You write the pointer-address of the array into the stream ... i dont't think thats your goal.
If you want to put the content of the array into the stream you should use
msRecInfo.Write( arrOf[ Low( arrOf ) ], SizeOf( Integer ) * Length( arrOf ) );
Why? You have to point to the first position of the data (first element of the array), and you have to calculate the length of the data.
On the receiving part it is just the same
msRecInfo.Read( arrOf[ Low( arrOf ) ], SizeOf( Integer ) * Length( arrOf ) );
PS: This may work well in this special case, but to be safe, you should send at first, the length of all data, so the receiver knows, when message is complete
Upvotes: 3