⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 rtcdatacli.pas

📁 Delphi快速开发Web Server
💻 PAS
📖 第 1 页 / 共 5 页
字号:
{
  @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 + -