📄 rtcdatacli.pas
字号:
{
@html(<b>)
Data Client Components
@html(</b>)
- Copyright (c) Danijel Tkalcec
@html(<br><br>)
This unit implements a set of Client-side Data components. @Link(TRtcDataClient)
is the base class for all Request/Response based client connections. It implements the
mechanisms used by components like @Link(TRtcDataClientLink) @Link(TRtcDataRequest) and
@Link(TRtcClientModule) and so prepares the road for different connection providers,
which will all be able to use all higher-level RTC components. You won't be creating
TRtcDataClient components, since it only implements the mechanism for working with
other Data-related RTC components, but it doesn't implement a communication protocol.
First RTC component which implements the connection is @Link(TRtcHttpClient). You should
use that component if you want to communicate with a Http Server over TCP/IP.
}
unit rtcDataCli;
{$INCLUDE rtcDefs.inc}
interface
uses
Windows,Messages,
Classes,SysUtils,
{$IFNDEF FPC}
Forms,
{$ENDIF}
rtcInfo,
rtcConn,
memObjList,
rtcSyncObjs,
rtcThrPool;
type
{ @abstract(Basic Request Info)
Objects of this type (inherited) are created for every request passed to DataClient
and destroyed by DataClient after the Request has been processed or rejected.
@exclude }
TRtcClientRequestInfo=class
private
FWasInjected: boolean;
FWasAborted: boolean;
public
// @exclude
procedure Call_ResponseDone(Sender:TRtcConnection); virtual; abstract;
// @exclude
procedure Call_ResponseData(Sender:TRtcConnection); virtual; abstract;
// @exclude
procedure Call_ResponseAbort(Sender:TRtcConnection); virtual; abstract;
// @exclude
procedure Call_ResponseReject(Sender:TRtcConnection); virtual; abstract;
// @exclude
procedure Call_BeginRequest(Sender:TRtcConnection); virtual; abstract;
// @exclude
procedure Call_DataOut(Sender:TRtcConnection); virtual; abstract;
// @exclude
procedure Call_DataIn(Sender:TRtcConnection); virtual; abstract;
// @exclude
procedure Call_DataSent(Sender:TRtcConnection); virtual; abstract;
// @exclude
procedure Call_ReadyToSend(Sender:TRtcConnection); virtual; abstract;
// @exclude
procedure Call_SessionOpen(Sender:TRtcConnection); virtual; abstract;
// @exclude
procedure Call_SessionClose(Sender:TRtcConnection); virtual; abstract;
// @exclude
procedure Call_DataReceived(Sender:TRtcConnection); virtual; abstract;
// @exclude
procedure Call_ConnectLost(Sender:TRtcConnection); virtual; abstract;
// @exclude
procedure Call_RepostCheck(Sender:TRtcConnection); virtual; abstract;
// @exclude
function Get_Request:TRtcClientRequest; virtual; abstract;
// @exclude
property WasInjected:boolean read FWasInjected write FWasInjected default false;
// @exclude
property WasAborted:boolean read FWasAborted write FWasAborted default false;
end;
{ @abstract(Client Session) }
TRtcClientSession=class(TRtcSession)
protected
// @exclude
FCon:TRtcConnection;
public
// @exclude
procedure Init;
// Open a new session with this ID
procedure Open(const _ID:string); virtual;
procedure Close; virtual;
end;
// @abstract(All Components used by the DataClient are derived from this class)
TRtcClientComponent = class(TRtcComponent);
TRtcAbsDataClientLink = class; // forward
// @exclude
TRtcDataClientLinkList = class
private
FList:TList;
public
constructor Create;
destructor Destroy; override;
procedure Add(Value:TRtcAbsDataClientLink);
procedure Remove(Value:TRtcAbsDataClientLink);
procedure RemoveAll;
function Count:integer;
function Get(index:integer):TRtcAbsDataClientLink;
end;
{ @Abstract(Data Client Connection component)
Most of the events published by TRtcDataClient will NOT be defined directly on the
@Link(TRtcDataClient), but instead of that will be passed on to posted DataRequest components,
so that each DataRequest can process its request (send request out and accept response).
Posted requests will be processed one-by-one, even if they are posted all at the same time.
Properties to check first:
@html(<br>)
@Link(TRtcConnection.ServerAddr) - Address to connect to
@html(<br>)
@Link(TRtcConnection.ServerPort) - Port to connect to
@html(<br><br>)
Methods to check first:
@html(<br>)
@Link(TRtcClient.Connect) - Connect to Server
@html(<br>)
@Link(TRtcDataClient.Request), @Link(TRtcDataClient.WriteHeader), @Link(TRtcConnection.Write) - Write result to server
@html(<br>)
@Link(TRtcDataClient.Response), @Link(TRtcConnection.Read) - Read Server's response
@html(<br>)
@Link(TRtcConnection.Disconnect) - Disconnect from Server
@html(<br><br>)
Events to check first:
@html(<br>)
@Link(TRtcConnection.OnConnect) - Connected to Server
@html(<br>)
@Link(TRtcConnection.OnDataSent) - Data sent to server (buffer now empty)
@html(<br>)
@Link(TRtcDataClient.OnResponseAbort) - Connection Lost while receiving response, but Repost was not triggered.
@html(<br>)
@Link(TRtcConnection.OnDisconnect) - Disconnected from Server
@html(<br><br>)
Check @Link(TRtcClient) and @Link(TRtcConnection) for more info.
}
TRtcDataClient = class(TRtcClient)
private
FOnRepostCheck:TRtcNotifyEvent;
FOnBeginRequest:TRtcNotifyEvent;
FOnResponseDone:TRtcNotifyEvent;
FOnResponseData:TRtcNotifyEvent;
FOnResponseAbort:TRtcNotifyEvent;
FOnResponseReject:TRtcNotifyEvent;
FOnSessionOpen:TRtcNotifyEvent;
FOnSessionClose:TRtcNotifyEvent;
FMayInsertRequest:boolean;
FRequestInserted:boolean;
FRequestSkipped:integer;
FAutoConnect:boolean;
FCS:TRtcCritSec;
FSession:TRtcClientSession;
FRequestList:TList;
FActiveRequest:TRtcClientRequestInfo;
FIdle:boolean;
FDataClientLinks:TRtcDataClientLinkList;
FMyRequest, FRequest:TRtcClientRequest;
FMyResponse, FResponse:TRtcClientResponse;
procedure CheckRequestSkipped;
function GetAutoConnect: boolean;
procedure SetAutoConnect(const Value: boolean);
protected
// @exclude
function isConnectionRequired:boolean; override;
// @exclude
procedure AddDataClientLink(Value:TRtcAbsDataClientLink);
// @exclude
procedure RemoveDataClientLink(Value:TRtcAbsDataClientLink);
// @exclude
procedure RemoveAllDataClientLinks;
// @exclude
procedure SetRequest(const Value: TRtcClientRequest); virtual;
// @exclude
procedure SetResponse(const Value: TRtcClientResponse); virtual;
// @exclude
procedure CallConnect; override;
// @exclude
procedure CallDataReceived; override;
// @exclude
procedure CallDataOut; override;
// @exclude
procedure CallDataIn; override;
// @exclude
procedure CallDataSent; override;
// @exclude
procedure CallReadyToSend; override;
// @exclude
procedure CallConnectLost; override;
// @exclude
procedure CallConnectFail; override;
// @exclude
procedure CallConnectError(E: Exception); override;
// @exclude
procedure AddRequest(Req:TRtcClientRequestInfo);
// @exclude
procedure RemoveAllRequests;
// @exclude
procedure RemoveRequest;
// @exclude
procedure StartRequest;
// @exclude
procedure PrepareNextRequest;
// @exclude
function CheckRequestWork:boolean;
{ Insert a Request before active request.
This procedure may ONLY be called from BeginRequest event
to place another request before the active request. }
procedure InsertRequest(Req:TRtcClientRequestInfo); virtual;
{ Post a new Request Object.
When posting from inside a RTC event or a remote function,
"FromInsideEvent" parameter has to be TRUE to avoid memory consumption. }
procedure PostRequest(Req:TRtcClientRequestInfo; FromInsideEvent:boolean=False); virtual;
{ Post a 'StartRequest' Job }
procedure PostStartRequest;
{ Check if connection is Idle (no requests waiting or disconnected)
@exclude }
function isIdle:boolean; virtual;
public
// @exclude
constructor Create(AOwner: TComponent); override;
// @exclude
destructor Destroy; override;
// @exclude
procedure CallBeginRequest; virtual;
// @exclude
procedure CallResponseDone; virtual;
// @exclude
procedure CallResponseData; virtual;
// @exclude
procedure CallResponseAbort; virtual;
// @exclude
procedure CallResponseReject; virtual;
// @exclude
procedure CallSessionOpen; virtual;
// @exclude
procedure CallSessionClose; virtual;
// @exclude
procedure CallRepostCheck; virtual;
{ Flush all buffered data.
@html(<br>)
When using 'Write' without calling 'WriteHeader' before, all data
prepared by calling 'Write' will be buffered until your event
returns to its caller (automatically upon your event completion) or
when you first call 'Flush'. Flush will check if Request.ContentLength is set
and if not, will set the content length to the number of bytes buffered.
@html(<br>)
Flush does nothing if WriteHeader was called for this response.
@exclude}
procedure Flush; virtual; abstract;
// You can call WriteHeader to send the Request header out.
procedure WriteHeader(SendNow:boolean=True); overload; virtual; abstract;
{ You can call WriteHeader with empty 'HeaderText' parameter to
tell the component that you do not want any HTTP header to be sent. }
procedure WriteHeader(const HeaderText: string; SendNow:boolean=True); overload; virtual; abstract;
// Skip all requests (RequestAborted events will NOT BE triggered!!!)
procedure SkipRequests; virtual;
// Cancel all requests (will fire all RequestAborted events)
procedure CancelRequests; virtual;
// Check request count (number of requests waiting to be processed)
function RequestCount:integer; virtual;
// Wait for all posted requests and function calls to complete,
// be aborted, be calceled, or for the connection to close. @html(<br>)
// Using a timeout (seconds) you can specify how long you want to wait.
// (0=forever). Returns TRUE only if there are no more requests waiting.
// Returns FALSE if timed-out or terminating or connection can't be open
// or connection closed on purpose.
function WaitForCompletion(UserInteractionAllowed:boolean=False; _Timeout:cardinal=0):boolean; virtual;
{ This connection's Session info.
If you will be using multiple client connections and
need to store session information, you have to start a
separate session for every client connection.
@html(<br><br>)
There is a difference between this DataClient's Session object and
the DataServer's Session object. DataClient's Session object stays
permamently defined all the time, from the point of object creation to its destruction.
Different from the Server connection, }
property Session:TRtcClientSession read FSession;
// Another request has just been inserted before this one.
property RequestInserted:boolean read FRequestInserted;
{ Access to current request information.
Use Request property to prepare the request header.
Here you can set all header variables and parameters.
If there is no content body to send out (only header), you will at
least have to call 'WriteHeader', or 'Write' without parameters once. }
property Request:TRtcClientRequest read FRequest write SetRequest;
{ Access to current response information.
Use Response property to read the response information received.
Here is all the info that was available in response header.
To read response's body, use the Read function. }
property Response:TRtcClientResponse read FResponse write SetResponse;
published
{ You have two ways of working with connections. One is to open a connection
on application start by calling "Connect" and using "ReconnectOn" to keep the
connection open until your application closes, the other is to use implicit
connects when a connection is required (when you post a request of any kind). @html(<br>)
You should set AutoConnect to TRUE if you do not want your connection to remain
open all the time, but also do not want to call "Connect" when you need a connection.
For connection to remain open as long as there are requests waiting to be processed,
you will also need to set the appropriate ReconnectOn parameters. @html(<br><br>)
When AutoConnect is TRUE, connection will be automaticaly opened when you
post a request (no need to call Connect) and "ReconnectOn" parameters will be
used to reconnect only if there are requests waiting in the queue. By using Timeout
parameters, your connection will close after specified timeout and stay closed
until needed again, when a new connection will be automaticaly initiated. @html(<br><br>)
When AutoConnect is FALSE, connection has to be opened explicitly by calling "Connect"
and "ReconnectOn" parameters will be used to keep the connection open, even if there
are no requests waiting in the queue. }
property AutoConnect:boolean read GetAutoConnect write SetAutoConnect default False;
{ Called before each request start.
You can use this event to prepare the request. }
property OnBeginRequest:TRtcNotifyEvent read FOnBeginRequest write FOnBeginRequest;
{ Called after the last DataReceived event for the response, when a response is done.
You can use this event to react to the final response. }
property OnResponseDone:TRtcNotifyEvent read FOnResponseDone write FOnResponseDone;
{ Called every time a data package comes from Server, immediatelly after OnDataReceived. }
property OnResponseData:TRtcNotifyEvent read FOnResponseData write FOnResponseData;
{ Called after OnConnectLost,OnConnectFail and OnConnectError if Request was not reposted. }
property OnRepostCheck:TRtcNotifyEvent read FOnRepostCheck write FOnRepostCheck;
{ Called after OnRepostCheck if Request was not reposted. }
property OnResponseAbort:TRtcNotifyEvent read FOnResponseAbort write FOnResponseAbort;
{ Called if response has been rejected by calling Response.Reject. }
property OnResponseReject:TRtcNotifyEvent read FOnResponseReject write FOnResponseReject;
{ Called after a new session has been opened. }
property OnSessionOpen:TRtcNotifyEvent read FOnSessionOpen write FOnSessionOpen;
{ Called before an existing session is about to get closed. }
property OnSessionClose:TRtcNotifyEvent read FOnSessionClose write FOnSessionClose;
{ This event will be triggered every time a chunk of your data
prepared for sending has just been sent out. To know
exactly how much of it is on the way, use the @Link(TRtcConnection.DataOut) property.
@html(<br><br>)
NOTE: Even though data has been sent out, it doesn't mean that
the other side already received it. It could also be that connection will
break before this package reaches the other end. }
property OnDataOut;
{ This event will be triggered every time a chunk of data
has just come in (received). To know exactly how much of it
has just arrived, use the @Link(TRtcConnection.DataIn) property. }
property OnDataIn;
end;
TRtcDataClientLink=class; // forward
{ @abstract(DataClient Link wrapper) }
TRtcAbsDataClientLink=class(TRtcClientComponent)
private
FClient: TRtcDataClient;
FLink: TRtcDataClientLink;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -