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

📄 rtcclimodule.pas

📁 Delphi快速开发Web Server
💻 PAS
📖 第 1 页 / 共 5 页
字号:
{
  @html(<b>)
  Client Module for Remote Functions
  @html(</b>)
  - Copyright (c) Danijel Tkalcec
  @html(<br><br>)

  This unit introduces @Link(TRtcClientModule), the client-side component for ENABLING remote functions.
  By using @Link(TRtcClientModule), you can easily call remote server functions and get results
  in form of objects, passed to the event handlers you define. Also, by assigning a
  @Link(TRtcFunctionGroup) component to your @Link(TRtcClientModule) component,
  server can (as a result of any remote function call from the client) return functions which
  will be executed on the client side before the result object is passed to the local event handler.
  Implementing a RTC Remote Function is as easy as writing a local function.
}

unit rtcCliModule;

{$INCLUDE rtcDefs.inc}

interface

uses
  Classes,
  Windows,
  SysUtils,

  rtcTimer,
  rtcInfo,
  rtcConn,
  rtcCrypt,
  rtcSyncObjs,

  rtcDataCli,
  rtcFunction,

{$IFDEF COMPRESS}
  rtcZLib,
{$ENDIF}

  rtcFastStrings,
  memObjList;

type
  // @exclude
  EPostInteractive = class(EAbort);

  // @exclude
  TRtcInteractiveResult = class
  public
    FEvent:TRtcResult;
    Data,Result:TRtcValue;

    destructor Destroy; override;
    end;

  { @abstract(Used to store all calls for a single Post from a ClientModule)

    @exclude }
  TRtcClientModuleCallsArray=class(TRtcArray)
  private
    FEvents:array of TRtcResult;

    function GetEvent(index: integer): TRtcResult;
    procedure SetEvent(index: integer; const _Value: TRtcResult);

  public
    constructor Create; override;
    destructor Destroy; override;

    property Event[index:integer]:TRtcResult read GetEvent write SetEvent;
    end;

  // @exclude
  TRtcCryptClient=class(TRtcObject)
  public
    HaveHello,HaveStart:boolean;
    ControlCounter:integer;
    ClientHello,ServerHello,
    ClientKey,ServerKey,
    ControlKey:string;

    Read,Write:TRtcCrypt;

    destructor Destroy; override;

    procedure Init;
    procedure Kill; override;
    end;

  // @exclude
  TRtcClientModuleData=class
  public
    FRequest:TRtcClientRequest;
    FData:TRtcValue;
    FPostLevel:integer;
    FCalls:TRtcClientModuleCallsArray;

    constructor Create; virtual;
    destructor Destroy; override;
    end;

  { @abstract(Use to call remote functions and receive their results)

    ClientModule is used to prepare remote function calls, post them to
    the server, accept server's response and call local event handlers with
    the result received for each call. You can post a single call or multiple
    function calls with one request, or even use a timer-based trigger to post
    all the requests prepared until now. }
  TRtcClientModule=class(TRtcAbsDataClientLink)
  private
    FMyData:TObjList;
    FMainThrData:TRtcClientModuleData;

    FIntCS:TRtcCritSec;
    FCS:TRtcCritSec;

    FIntTimer:TRtcTimer;
    FIntRes:TList;

    FFunctions:TRtcFunctionGroup;
    FRelease:boolean;

    FModuleFileName:string;
    FModuleHost:string;
    FAutoRepost:integer;
    FAutoSessions: boolean;


    FOnBeginRequest: TRtcNotifyEvent;
    FOnResponseAbort: TRtcNotifyEvent;
    FOnResponseDone: TRtcNotifyEvent;
    FOnResponseReject: TRtcNotifyEvent;
    FOnConnectLost: TRtcNotifyEvent;
    FOnSessionExpired: TRtcNotifyEvent;
    FOnRepostCheck: TRtcNotifyEvent;

    FOnSessionOpen: TRtcNotifyEvent;
    FOnSessionClose: TRtcNotifyEvent;

    FAutoEncrypt: integer;
    FForceEncrypt: boolean;
    FOnResponseError: TRtcNotifyEvent;
    FSecureKey: string;

    {$IFDEF COMPRESS}
    FCompress: TRtcCompressLevel;
    {$ENDIF}

    FOnNoEncryption: TRtcNotifyEvent;
    FOnNeedEncryption: TRtcNotifyEvent;
    FOnWrongEncryption: TRtcNotifyEvent;

    FHyperThreading: boolean;
    FDataFormat: TRtcDataFormat;

    function CheckMyData:TRtcClientModuleData;
    function GetMyData:TRtcClientModuleData;
    procedure ClearMyData;

    function IsRemoteCallRequest(Sender:TRtcConnection):boolean;

    procedure NotifyResultAborted(Sender:TRtcConnection);

    procedure Response_Problem(Sender:TRtcConnection);

    procedure Call_SessionExpired(Sender:TRtcConnection);
    procedure Call_NoEncryption(Sender:TRtcConnection);
    procedure Call_NeedEncryption(Sender:TRtcConnection);
    procedure Call_WrongResponse(Sender:TRtcConnection);
    procedure Call_WrongEncryption(Sender:TRtcConnection);

    function GetCrypt(Session:TRtcSession):TRtcCryptClient;
    procedure NewCrypt(Session:TRtcSession);
    procedure DelCrypt(Session:TRtcSession);

    function GetFunctionGroup: TRtcFunctionGroup;
    procedure SetFunctionGroup(const Value: TRtcFunctionGroup);

    function GetModuleFileName: string;
    procedure SetModuleFileName(const Value: string);

    function GetModuleHost: string;
    procedure SetModuleHost(const Value: string);
    procedure SetAutoEncrypt(const Value: integer);
    procedure SetAutoSessions(const Value: boolean);
    procedure SetForceEncrypt(const Value: boolean);

    procedure PostInteractiveResult(Event:TRtcResult; Data,Result:TRtcValue);

    procedure DoInteractiveResult;

    function GetData: TRtcValue;
    function GetPostLevel: integer;
    function GetRequest: TRtcClientRequest;

    {$IFDEF COMPRESS}
    procedure SetCompress(const Value: TRtcCompressLevel);
    procedure SetDataFormat(const Value: TRtcDataFormat);
    {$ENDIF}

  protected
    // @exclude
    procedure Call_BeginRequest(Sender:TRtcConnection); override;
    // @exclude
    procedure Call_ResponseDone(Sender:TRtcConnection); override;
    // @exclude
    procedure Call_ResponseData(Sender:TRtcConnection); override;
    // @exclude
    procedure Call_ResponseAbort(Sender:TRtcConnection); override;
    // @exclude
    procedure Call_ResponseReject(Sender:TRtcConnection); override;

    // @exclude
    procedure Call_SessionOpen(Sender:TRtcConnection); override;
    // @exclude
    procedure Call_SessionClose(Sender:TRtcConnection); override;

    // @exclude
    procedure Call_DataReceived(Sender:TRtcConnection); override;

    // @exclude
    procedure Call_DataOut(Sender:TRtcConnection); override;
    // @exclude
    procedure Call_DataIn(Sender:TRtcConnection); override;
    // @exclude
    procedure Call_DataSent(Sender:TRtcConnection); override;
    // @exclude
    procedure Call_ReadyToSend(Sender:TRtcConnection); override;
    // @exclude
    procedure Call_ConnectLost(Sender:TRtcConnection); override;
    // @exclude
    procedure Call_RepostCheck(Sender:TRtcConnection); override;

  public
    // @exclude
    constructor Create(AOwner:TComponent); override;
    // @exclude
    destructor Destroy; override;

    // You can call this method from Interactive result function to destroy the ClientModule object.
    procedure Release;

    { If you want to post multiple calls in one request (and not each in its own request),
      you can use the "StartCalls" methods to open a new "call transaction", which is closed
      by a call to "Post". Each "StartCalls" has to be closed by a call to "Post". Using
      StartCalls/Call/Call/Post, you can combine a number of remote calls in one request.
      Another thing you don't have to worry about when using StartCalls/Post is clearing
      of the Data object in case of an exception during Data preparation. }
    procedure StartCalls; virtual;

    { After you have used the "Data" property to prepare the objects and functions
      you want to send to the server, use the "Call" method to define the event handler
      which has to receive the result after the data has been posted to the server.
      If you are not interested in the result values of your request, but just
      want to send this to the server as a "procedure call" and ignore the result,
      you can use "Call(nil)" and any result received will be simply thrown away.
      A result WILL be received in any case, which ensures that the function was executed.
      But, even in case of an exception, result with be ignored. @html(<br><br>)

      After "Call()", an object will be created to hold the prepared "Data" with a
      pointer to the TRtcResult component, after which the Data property will be cleared,
      so that you can prepare and Call new remote functions immediatelly. @html(<br><br>)

      If you are calling a remote function from inside other remote functions event,
      you should make the Call with "FromInsideEvent" parameter set to TRUE.

      If you didn't start a separate call transaction using "StartCalls", your call will be
      automaticaly posted to the server in a single request. To post multiple calls in
      one request, simply call "StartCalls" before, prepare "Data" before each "Call" and
      after the last call, use the "Post" method to send everything out. }
    procedure Call(ResultHandler:TRtcResult;FromInsideEvent:boolean=False); overload;

    { "Post" will decrease the call transaction level which was increated by a "StartCalls"
      and if the last StartCalls was closed by calling this Post, an object will be created
      to hold the prepared Request info and a list of remote calls will be sent to the server
      if connection with server is established. @html(<br><br>)

      If you are calling a remote function from inside other remote functions event,
      you should make the Call with "FromInsideEvent" parameter set to TRUE.

      Events assigned to this TRtcClientModule will not be removed nor cleared,
      so you can define them at design-time and not worry about them at runtime. }
    procedure Post(FromInsideEvent:boolean=False); virtual;

    { ONLY use this Request property if you need to prepare a request BEFORE posting.
      @html(<br>)
      DO NOT directly use this property when processing function calls.
      After a request has been posted, it is moved to the DataClient,
      so you can access it (from events) using Sender's Request property. }
    property Request:TRtcClientRequest read GetRequest;

    { To prepare a remote call, use this "Data" property to define and/or assign object(s)
      you want to send to the server. After you have defined the "Data" property to hold
      all information you want to send to the server, use the "Call" method to store that
      data and define the event handler to be used when a result is received. @html(<br>)
      ONLY use this Data property to prepare data for a remote call. @html(<br>)
      DO NOT directly use this property when processing the received result. }
    property Data:TRtcValue read GetData;

    { Using this property, you can check at which Calls level your ClientModule currently is.
      CallsLevel will be 0 outside of StartCalls, increased by 1 after each StartCalls
      and decreased by 1 after each Post. }
    property CallsLevel:integer read GetPostLevel;

  published
    {$IFDEF COMPRESS}
    { Use this property to define what compression level you want to use when sending
      data from Client to Server. Default Compression value is "cNone" (no compression).
      You can use different compression levels between client and server (for example,
      fast or no compression from client and max from server). If your client has to
      work with servers which don't support compression, you have to use "cNone". }
    property Compression:TRtcCompressLevel read FCompress write SetCompress default cNone;
    {$ENDIF}

    { Use this property to define what data format you want to use when sending data from
      Client to Server. If your client will only be talking to a Server written using RTC
      components, it is recommended to use "fmt_RTC", which upports all RTC data types, automatic
      compression, automatic encryption and automatic Sessions, as well as nested function calls
      and sending multiple function calls in a single request. On the other hand, if your client
      has to communicate with a Server which does NOT support the RTC format, you can use
      the "fmt_XMLRPC" format, which will work with Servers implementing the XML-RPC format. @html(<br><br>)
      NOTE: Since advanced functionality like automatic Compression, auto Encryption
      and automatic Sessions are native to the RTC format and other Servers do not implement it,
      those advanced properties will be ignored and assumed to be FALSE with all formats other
      then the "fmt_RTC" format. If you need those advanced options, use the "fmt_RTC" format. }
    property DataFormat:TRtcDataFormat read FDataFormat write SetDataFormat default fmt_RTC;

    { If you want to enable the possibility to use this Client Module to send remote function
      calls from multiple threads AT THE SAME TIME, where this component will acs as if
      it were X components, one for each thread, simply set HyperThreading to TRUE. @html(<br><br>)

      This is useful if you need to send remote function calls to another Server from
      within your Server running in multi-threaded mode and want to use only one set of
      rtcHttpClient/rtcClientModule components for all clients connected to your Server.
      Even in HyperThreading mode, only properties and methods needed to prepare and post remote
      function calls (Data, Request, Call, StartCalls, PostLevel and Post) will use a separate
      copy for each thread, while all other properties and methods exist only once for all threads,
      so don't try to modify them while your application is actively using the component in
      multi-threaded mode. @html(<br><br>)

      Leave HyperThreading as FALSE to use this component "the stadard way" (for example,
      when you're writing a client application where remote calls are done from the main thread
      or if you are creating a separate component for every thread that needs it). }
    property HyperThreading:boolean read FHyperThreading write FHyperThreading default False;

    { Set this property to a value other than 0 if you want to use automatic Encryption
      with a random generated key of "EncryptKey" bytes. One byte stands for
      encryption strength of 8 bits. For strong 256-bit encryption, use 32 bytes. @html(<br><br>)

      The final encryption key is combined from a client-side key and a key
      received from the server, where server decides about its encryption strength.
      If server doesn't support Encryption, data will not be encrypted,
      regardless of the value you use for AutoEncrypt. @html(<br><br>)

      EncryptionKey uses sessions to keep the encryption keys. When you set EncryptionKey
      to a value other than 0 (turn it ON), AutoSessions will be set to TRUE.
      Also, setting AutoSessions to FALSE will set EncryptionKey to 0 (OFF). }
    property EncryptionKey:integer read FAutoEncrypt write SetAutoEncrypt default 0;

    { If you need a 100% secure connection, define a Secure Key string
      (combination of letters, numbers and special characters) for each
      ServerModule/ClientModule pair, additionally to the EncryptionKey value.
      ClientModule will be able to communicate with the ServerModule ONLY if
      they both use the same SecureKey. Default value for the SecureKey is
      an empty string (means: no secure key). @html(<br><br>)

      SecureKey will be used in the encryption initialisation handshake,
      to encrypt the first key combination sent by the ClientModule.
      Since all other data packages are already sent using some kind of encryption,
      by defining a SecureKey, you encrypt the only key part which would have
      been sent out without special encryption. }
    property SecureKey:string read FSecureKey write FSecureKey;

    { Setting this property to TRUE will tell the ClientModule to work with the
      Server ONLY if Server supports encryption. If AutoEncryptKey is > 0 and
      server doesn't support encryption, function calls will not be passed to
      the server and any response coming from the server will be rejected, until
      server enables encryption. }
    property ForceEncryption:boolean read FForceEncrypt write SetForceEncrypt default False;

    { Set this property to TRUE if you want ClientModule to request a new session
      automatically if the Session.ID is not set when posting a request.
      Session handling is built into the ClientModule and uses Request.Params to
      send the Session.ID to the server and Response.Cookie['ID'] to receive a
      new session ID from the server and initialize the session object. @html(<br><br>)

      Since session ID's are communicated automaticaly by the ClientModule and
      ServerModule components, all TRtcFunction and TRtcResult components used by
      this ClientModule will have direct access to the session object.
      When AutoSessions is set to true, a new session will be requested if
      no session exists or when a session expires. @html(<br><br>)

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -