Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Porting old Delphi2007 application using TServerSocket....
#16
Coming back to this after fixing GUI component problems in porting to FPC/Lazarus on Linux...

A few additional questions:
1. Data container
As you pointed out the old code uses string as the container even though the data is not necessarily textual.
So if I were to change that to use TIdBytes instead, what would the read function in the thread look like?
Now with FRxData declared as string:

Code:
FRxData: string;
...
FRxData := FConn.IOHandler.ReadString(FConn.IOHandler.InputBuffer.Size);

If I instead use TIdBytes is the read function going to look like this?
Code:
FRxData: TIdBytes;
...
FConn.IOHandler.ReadBytes(FRxData, FConn.IOHandler.InputBuffer.Size, false);
Do I have to set the size of FRxData before calling the read or is that handled inside the read?

2. Sending the data via the event
Is the synchronized call to the event handler getting a copy of FRxData or FRxData itself?
Does it have to set back the length of the container to zero after processing the data?

Right now I have the code at the bottom, which compiles but the application itself does not because I have not yet gotten rid of the TClientSocket stuff coming from the Delphi7 implementation...

3. How to handle the socket messages?
I am looking for how to handle the various events existing in the old code coming from TClientSocket and in need of an Indy implementation..

This is the section of code I ave problem with there. As you can see I have as yet just copied the Delphi code into a section inside a conditional.
Code:
    {$IFDEF FPC}
      procedure OnClientError(Sender: TObject; Socket: TIdTCPClient; ErrorEvent: TErrorEvent; var ErrorCode: Integer);
      procedure OnSocketConnect(Sender: TObject; Socket: TIdTCPClient);
      procedure OnSocketDisconnect(Sender: TObject; Socket: TIdTCPClient);
      procedure OnSocketRead(Sender: TObject; Socket: TIdTCPClient);
    {$ELSE}
      procedure OnClientError(Sender: TObject; Socket: TCustomWinSocket; ErrorEvent: TErrorEvent; var ErrorCode: Integer);
      procedure OnSocketConnect(Sender: TObject; Socket: TCustomWinSocket);
      procedure OnSocketDisconnect(Sender: TObject; Socket: TCustomWinSocket);
      procedure OnSocketRead(Sender: TObject; Socket: TCustomWinSocket);
    {$ENDIF}
The compiler stops at the first line complaining about the missing identifier:
Code:
class_SSRemoteClient.pas(110,82) Error: Identifier not found "TErrorEvent"

I have looked at the documentation and found that there is an event OnStatus that gets fired for any status change, so maybe that could replace all 4 of the above separate events?
Something like this:

Code:
procedure TTcpClientComm.OnStatusChange(ASender: TObject; const AStatus: TIdStatus; const AStatusText: string);
{This procedure parses the socket status change messages, what to do in the respective cases?}
begin
  case AStatus of
    hsResolving: ;
    hsConnecting: ;
    hsConnected: ;  //Create the read thread here?
    hsDisconnecting: ;
    hsDisconnected: ;  //Terminate the read thread here?
    hsStatusText: ;  //What is this accomplishing?
  end;
end;

constructor TTcpClientComm.Create;
begin
  FConn := TIdTCPClient.Create(NIL);
  FConn.OnStatus := OnStatusChange;
end;

However, I don't really know if this is the correct place.....
There seems also to be other events available when looking in Lazarus code completion, here are all I found:
Code:
OnAfterBind
OnBeforeBind
OnConnected
OnDisconnected
OnSocketAllocated
OnStatus
OnWork
OnWorkBegin
OnWorkEnd
Which one should I use for example in order to create and terminate the read thread?
Notice that I know when the socket is commanded to connect but not necessarily when it is disconnected since that can be commanded from the client side.


My current implementation of the event driven data reception class using TIdTcpClient shown below.
Does it look sensible now, especially how the thread is created and destroyed in connect/disconnect?

Code:
{$IFDEF FPC}
  {$MODE Delphi}
{$ENDIF}

interface

uses
  Classes,
  SysUtils,
  IdBaseComponent,
  IdComponent,
  IdTCPConnection,
  IdTCPClient,
  IdGlobal
  ;

type
  TCommRxEvent = procedure (Sender: TObject; Data: TIdBytes) of object;

  { TReadingThread }

  TReadingThread = class(TThread)
  protected
    FConn: TIdTCPConnection;
    FOnRxData: TCommRxEvent;
    FRxData: TIdBytes;
    procedure Execute; override;
    procedure DoOnRxData;
  public
    constructor Create(ACon: TIdTCPConnection);
    property OnRxData: TCommRxEvent read FOnRxData write FOnRxData;
  end;

  { TTcpClientComm }

  TTcpClientComm = class(TObject)
  private
    FConn: TIdTCPClient;
    FReadThread: TReadingThread;
    FOnRxData: TCommRxEvent;
    FLastError: string;
    function GetConnected: boolean;
    procedure OnStatusChange(ASender: TObject; const AStatus: TIdStatus; const AStatusText: string);
  public
    constructor Create;
    destructor  Destroy; override;
    property OnRxData: TCommRxEvent read FOnRxData write FOnRxData;
    property LastError: string read FLastError;
    property Connected: boolean read GetConnected;
    procedure Connect(Host: string; Port: word);
    procedure Disconnect;
    function  WriteString(Data: string): boolean; overload;
    function  WriteData(Data: TIdBytes): boolean; overload;
  end;

implementation

{ TTcpClientComm }


function TTcpClientComm.GetConnected: boolean;
begin
  Result := FConn.Connected;
end;

procedure TTcpClientComm.OnStatusChange(ASender: TObject; const AStatus: TIdStatus; const AStatusText: string);
{This procedure parses the socket status change messages, what to do in the respective cases?}
begin
  case AStatus of
    hsResolving: ;
    hsConnecting: ;
    hsConnected: ;
    hsDisconnecting: ;
    hsDisconnected: ;
    hsStatusText: ;
  end;
end;

constructor TTcpClientComm.Create;
begin
  FConn := TIdTCPClient.Create(NIL);
  FConn.OnStatus := OnStatusChange;
end;

destructor TTcpClientComm.Destroy;
begin
  if Connected then
    Disconnect;
  inherited Destroy;
end;

procedure TTcpClientComm.Connect(Host: string; Port: word);
begin
  FConn.Connect(Host, Port);
  FReadThread := TReadingThread.Create(FConn);
end;

procedure TTcpClientComm.Disconnect;
begin
  FReadThread.Terminate;
  FConn.Disconnect;
end;

function TTcpClientComm.WriteString(Data: string): boolean;
var
  DataBytes: TIdBytes;
begin
  Result := false;
  if Length(Data) = 0 then
  begin
    FLastError := 'No data';
    exit;
  end;
  SetLength(DataBytes, Length(Data));
  Move(Data[1], DataBytes[0], Length(Data));
  Result := WriteData(DataBytes);
end;

function TTcpClientComm.WriteData(Data: TIdBytes): boolean;
begin
  Result := false;
  if Length(Data) = 0 then
  begin
    FLastError := 'No data';
    exit;
  end;

  if not FConn.Connected then
  begin
    FLastError := 'Not connected';
    exit;
  end;

  FLastError := '';
  try
    FConn.IOHandler.Write(Data);
    Result := true;
  except
    on E: Exception do
      FLastError := E.Message;
  end;
end;

{ TReadingThread }

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 ...
          FConn.IOHandler.ReadBytes(FRxData, FConn.IOHandler.InputBuffer.Size, true);
          // 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;

constructor TReadingThread.Create(ACon: TIdTCPConnection);
begin
  FConn := ACon;
  inherited Create(False);
end;

end.
Reply


Messages In This Thread
RE: Porting old Delphi2007 application using TServerSocket.... - by BosseB - 09-18-2020, 10:00 AM

Forum Jump:


Users browsing this thread: 1 Guest(s)