(07-07-2020, 10:33 AM)BosseB Wrote: I have tried to use an event function that contains the data but then I got an error from Lazarus.
That is because you are not using TThread.Synchronize() correctly. It needs to look like this instead:
Code:
type
TCommRxEvent = procedure (Sender: TObject; Data: string) of object;
{ TReadingThread }
TReadingThread = class(TThread)
protected
FConn: TIdTCPConnection;
FOnRxData: TCommRxEvent;
FRxData: string;
procedure Execute; override;
procedure DoOnRxData;
public
constructor Create(ACon: TIdTCPConnection);
property OnRxData: TCommRxEvent read FOnRxData write FOnRxData;
end;
...
procedure TReadingThread.Execute;
begin
while not Terminated do
begin
if FConn.Connected then
begin
if FConn.IOHandler.CheckForDataOnSource(10) then
begin
if FConn.IOHandler.InputBuffer.Size > 0 then
begin
// read bytes from FConn up to InputBuffer.Size bytes ...
FRxData := FConn.IOHandler.ReadString(FConn.IOHandler.InputBuffer.Size);
//
// process bytes as needed ...
if Assigned(FOnRxData) then
Synchronize(DoOnRxData);
end;
end;
end;
end;
end;
procedure TReadingThread.DoOnRxData;
begin
if Assigned(FOnRxData) then
FOnRxData(Self, FRxData);
end;
In Delphi ,you could do the following instead, but FreePasscal does not support this approach yet:
Code:
type
TCommRxEvent = procedure (Sender: TObject; Data: string) of object;
{ TReadingThread }
TReadingThread = class(TThread)
protected
FConn: TIdTCPConnection;
FOnRxData: TCommRxEvent;
FRxData: string;
procedure Execute; override;
public
constructor Create(ACon: TIdTCPConnection);
property OnRxData: TCommRxEvent read FOnRxData write FOnRxData;
end;
...
procedure TReadingThread.Execute;
begin
while not Terminated do
begin
if FConn.Connected then
begin
if FConn.IOHandler.CheckForDataOnSource(10) then
begin
if FConn.IOHandler.InputBuffer.Size > 0 then
begin
// read bytes from FConn up to InputBuffer.Size bytes ...
FRxData := FConn.IOHandler.ReadString(FConn.IOHandler.InputBuffer.Size);
//
// process bytes as needed ...
if Assigned(FOnRxData) then
begin
Synchronize(
procedure
begin
if Assigned(FOnRxData) then
FOnRxData(Self, FRxData);
end
);
end;
end;
end;
end;
end;
end;
(07-07-2020, 10:33 AM)BosseB Wrote:Quote:I will say, though, that you should not create the reading thread until after the TIdTCPClient has been connected first. And then terminate and free the thread when the TIdTCPClient is being disconnected.
How can I do that, i.e. how do I know the connect and disconnect happened?
You are the one calling Connect() and Disconnect(). So, simply don't create the thread until you have called Connect() and it has exited without raising an exception. Terminate() the thread before calling Disconnect(), and then finish freeing the thread after Disconnect() exits.
(07-07-2020, 10:33 AM)BosseB Wrote: Do you mean passing the data out in the event handler in a TIdBytes container?
Yes.
(07-07-2020, 10:33 AM)BosseB Wrote: Or should the event not contain the data and the reading be done by the handler itself?
No, the reading should be done before the event handler is called. Let the thread do its job. HOW the thread reads, and WHAT it passes to the event, depend on your particular needs. For instance, given the STX/ETX example you provided earlier, the thread could wait for STX to arrive, then read until ETX arrives, and then pass everything in between to the event.
(07-07-2020, 10:33 AM)BosseB Wrote: I.e. the event is just a Synchronized notification and the socket read is done in the event implementation instead?
No.
(07-07-2020, 10:33 AM)BosseB Wrote: Seems like it would be better keep that from the caller and just provide the received data itself, that is at least what I thought a better encapsulation.
Yes.