Componentes.Terceros.RemObj.../internal/5.0.23.613/1/RemObjects SDK for Delphi/Source/uROIndyUDPChannel.pas
david 2824855ea7 - Modificación del paquete RemObjects_Core_D10 para que sea un paquete de runtime/designtime (antes era designtime sólo)
- Recompilación en Delphi10 de todos los paquetes de RO para generar las DCU's en Lib\D10
- Recompilación en Delphi10 de todos los paquetes de DA para generar las DCU's en Lib\D10

git-svn-id: https://192.168.0.254/svn/Componentes.Terceros.RemObjects@9 b6239004-a887-0f4b-9937-50029ccdca16
2007-09-10 14:06:19 +00:00

657 lines
19 KiB
ObjectPascal

unit uROIndyUDPChannel;
{----------------------------------------------------------------------------}
{ RemObjects SDK Library - Indy Components }
{ }
{ compiler: Delphi 5 and up, Kylix 2 and up }
{ platform: Win32, Linux }
{ }
{ (c)opyright RemObjects Software. all rights reserved. }
{ }
{ Provided by Nico Schoemaker (nico.schoemaker@teamro.remobjects.com }
{ }
{ Using this code requires a valid license of the RemObjects SDK }
{ which can be obtained at http://www.remobjects.com. }
{----------------------------------------------------------------------------}
{$I RemObjects.inc}
interface
uses classes, SysUtils,SyncObjs,
IdUDPClient,
uROClient, uROClientIntf, uROAsyncResponseStorage, uROAsync, uROThread,
uROClasses;
type
TROIndyUDPChannel = class;
TROUDPResponseThread = class(TROThread)
private
FChannel: TROIndyUDPChannel;
FCriticalSection: TCriticalSection;
FReqUID: String;
protected
FRetrys: Integer;
FRespReceived: Boolean;
procedure DoTimeOut;
procedure DoEnd;
procedure Execute;override;
public
property RequestUID: String read FReqUID;
property ReceivedResponse: Boolean read FRespReceived;
property Channel: TROIndyUDPChannel read FChannel;
property CriticalSection: TCriticalSection read FCriticalSection;
constructor Create(const aName: string; aChannel: TROIndyUDPChannel; aRequestUID: String);
end;
TROUDPResponseThreadClass = class of TROUDPResponseThread;
TROAsyncTimeOutEvent = procedure (aRequistUID: String) of object;
TROIndyUDPChannel = class(TROTransportChannel, IROTransport, IROTCPTransport,
IROAsyncTransportChannel, IROAsyncResponseStorage)
private
fIndyClient: TIdUDPClient;
FRespStorage: TROSimpleAsyncResponseStorage;
FReqStorage: TROSimpleAsyncResponseStorage;
FRespBuffer: TStringList;
FRetrys: Integer;
FAsyncTimeOut: Integer;
FOnAsyncResponseTimeOut: TROAsyncTimeOutEvent;
FRunningThread: TROUDPResponseThread;
function GetHost: string;
function GetPort: integer;
procedure SetHost(const Value: string);
procedure SetPort(const Value: integer);
protected
FCriticalSection: TCriticalSection;
FIDSize: Integer;
{ IROTransport }
function GetTransportObject : TObject; override;
{ IROTCPTransport }
function GetClientAddress : string;
{ IROAsyncTransportChannel }
function InvokeRequest(aRequest:TStream; iGetResponse:boolean=true):string;virtual;
procedure RetrieveResponse(const iMessageID:string; aResponse:TStream);
procedure DoEndOfThread(Sender: TROUDPResponseThread);virtual;
procedure DoOnTerminateThread(Sender: TObject);virtual;
procedure DoAsyncThreadTimeOut(ReqUID: String);virtual;
procedure IntDispatch(aRequest, aResponse : TStream); override;
procedure IntSetServerLocator(aServerLocator: TROServerLocator); override;
procedure SendReq(aRequest: TStream;UID: String;AddToStorage: Boolean=true);virtual;
function GetResponseThreadClass: TROUDPResponseThreadClass;virtual;
// Setters
procedure SetRetrys(const Value: Integer);
procedure SetAsyncTimeOut(const Value: Integer);
procedure SetOnAsyncResponseTimeOut(const Value: TROAsyncTimeOutEvent);
// Helpers
procedure CleanUpStorages;virtual;
function GetNewMesgID: String;virtual;
function StripID(var aResponse: String): String;
procedure CopyStream(aSource,aTarget: TStream);
function CreateUDPClient : TIdUDPClient; virtual;
procedure AddRequestToStorage(aRequest: TStream;aRequestID: String);
public
property ResponseStorage: TROSimpleAsyncResponseStorage read FRespStorage {$IFNDEF FPC} implements IROAsyncResponseStorage {$ENDIF};
{$IFDEF FPC} // implements does not work in FPC
procedure AddResp(aResp: TROResponseItem);
procedure RemoveResp(aResp: TROResponseItem);
procedure RemoveRespByUID(aUID: String);
procedure DeleteResp(Index: Integer);
function GetResp(Index: Integer): TROResponseItem;
function GetRespByUID(aUID: String): TROResponseItem; deprecated;
function GetResponseByUID(aUID: String): TROResponseItem;
function IndexOfResp(aResp: TROResponseItem): Integer;
function CountOfResp: Integer;
procedure ClearStorage;
property ResponseItem[Index: Integer]: TROResponseItem read GetResp;
{$ENDIF}
property RequestStorage: TROSimpleAsyncResponseStorage read FReqStorage;
function ReceiveResp(aTimeOut: Integer): Boolean;virtual;
function CheckStatus(const iMessageID:string):boolean;
constructor Create(AOwner: TComponent);override;
Destructor Destroy;override;
published
property AsyncTimeOut: Integer read FAsyncTimeOut write SetAsyncTimeOut default 1000;
property OnAsyncResponseTimeOut: TROAsyncTimeOutEvent read FOnAsyncResponseTimeOut write SetOnAsyncResponseTimeOut;
property Retrys: Integer read FRetrys write SetRetrys;
property IndyClient : TIdUDPClient read fIndyClient;
property Port : integer read GetPort write SetPort;
property Host : string read GetHost write SetHost;
published
property SynchronizedProbing;
property OnSendStream;
property OnReceiveStream;
property ServerLocators;
property DispatchOptions;
property OnServerLocatorAssignment;
property ProbeServers;
property ProbeFrequency;
property OnBeforeProbingServers;
property OnAfterProbingServers;
property OnBeforeProbingServer;
property OnAfterProbingServer;
property OnLoginNeeded;
end;
implementation
uses IdUDPBase,IdGlobal;
const C_CLRBUFFER_TO = 50; // TimeOut reading the buffer for ClearRecvBuffer
C_READTIMEOUT = 'Time out reading server response %s.';
C_NORESPONSE = 'Response %s is not available yet.';
C_EADDREQUEST = 'Parameters of AddRequestToStorage not Assigned';
function Strm_StreamToStr(aStream: TStream): String;
var OldPos: Integer;
begin
result := '';
if not(Assigned(aStream)) then
Exit;
OldPos := aStream.Position;
aStream.Position := 0;
SetLength(result, aStream.Size);
if (aStream.Size>0)
then aStream.Read(result[1], aStream.Size);
aStream.Position := OldPos;
end;
//------------------------------------------------------------------------------
{ TROIndyUDPChannel }
//------------------------------------------------------------------------------
constructor TROIndyUDPChannel.Create(AOwner: TComponent);
begin
inherited;
FCriticalSection := TCriticalSection.Create();
FIndyClient := CreateUDPClient;
FRespBuffer := TStringList.Create;
FRespStorage := TROSimpleAsyncResponseStorage.Create(Self);
FReqStorage := TROSimpleAsyncResponseStorage.Create(Self);
FRetrys := 5;
FAsyncTimeOut := 1000;
// In case the result of GetNewMesgID change's in the future
FIDSize := Length(GetNewMesgID);
{$IFDEF DELPHI6UP}
fIndyClient.SetSubComponent(TRUE);
{$ENDIF}
end;
destructor TROIndyUDPChannel.Destroy;
begin
FreeAndNil(FCriticalSection);
FreeAndNil(FRespBuffer);
FreeAndNil(FRespStorage);
FreeAndNil(FReqStorage);
inherited;
end;
procedure TROIndyUDPChannel.CopyStream(aSource, aTarget: TStream);
var lMs: TMemoryStream;
lPos: Integer;
begin
if (not(Assigned(aSource))) or (not(Assigned(aTarget))) then
Exit;
lPos := aSource.Position;
try
if aTarget is TMemoryStream then
begin
TMemoryStream(aTarget).LoadFromStream(aSource);
end else
begin
lMs := TMemoryStream.Create;
try
lMs.LoadFromStream(aSource);
lMs.Position := 0;
lMs.SaveToStream(aTarget);
finally
lMs.Free;
end;
end;
finally
aSource.Position := lPos;
end;
end;
function TROIndyUDPChannel.CreateUDPClient: TIdUDPClient;
begin
result := TIdUDPClient.Create(Self);
result.Port := 8090;
result.Name := 'InternalIndyClient';
end;
function TROIndyUDPChannel.GetClientAddress: string;
begin
result := fIndyClient.Host;
end;
function TROIndyUDPChannel.GetTransportObject: TObject;
begin
result := Self;
end;
{-----------------------------------------------------------------------------
Procedure: TROIndyUDPChannel.IntDispatch
Author: Nico
Date: 22-apr-2003
Arguments: aRequest, aResponse: TStream
Result: None
Called by the framework to send and receive a request/response
- Send's the request and add's it to the Request storage
- Recieive's the response and add's it to the Response storage
- Passes the response to aResponse
-----------------------------------------------------------------------------}
procedure TROIndyUDPChannel.IntDispatch(aRequest, aResponse: TStream);
var lResp: TStream;
lRetry: Integer;
lUID: String;
begin
lRetry := 0;
try
lUID := GetNewMesgID;
lResp := nil;
SendReq(aRequest,lUID);
repeat
if ReceiveResp(IndyClient.ReceiveTimeout) then
begin
if FRespStorage.GetRespByUID(lUID) <> nil then
lResp := FRespStorage.GetRespByUID(lUID).Response;
end;
inc(lRetry);
until (lRetry > Retrys) or (lResp <> nil);
if lRetry > Retrys then
raise Exception.Create(Format(C_READTIMEOUT,[lUID]));
CopyStream(lResp,aResponse);
aResponse.Position := 0;
FRespStorage.RemoveRespByUID(lUID);
finally
FReqStorage.RemoveRespByUID(lUID);
CleanUpStorages;
end;
end;
{-----------------------------------------------------------------------------
Procedure: TROIndyUDPChannel.AddRequestToStorage
Author: Nico
Date: 22-apr-2003
Arguments: aRequest: TStream;aRequesID: String
Result: None
Adds a request item to the RequestStorage
-----------------------------------------------------------------------------}
procedure TROIndyUDPChannel.AddRequestToStorage(aRequest: TStream;aRequestID: String);
var lMs: TMemoryStream;
lReqItm: TROResponseItem;
begin
if (not(Assigned(aRequest))) or (aRequestID = '') then
raise Exception.Create(C_EADDREQUEST);
FCriticalSection.Enter;
try
lMS := TMemoryStream.Create;
CopyStream(aRequest,lMs);
lReqItm := TROResponseItem.Create(lMS,aRequestID);
FReqStorage.AddResp(lReqItm);
finally
FCriticalSection.Leave;
end;
end;
{-----------------------------------------------------------------------------
Procedure: TROIndyUDPChannel.ReceiveResp
Author: Nico
Date: 22-apr-2003
Arguments: None
Result: Boolean
Recieves a single response and saves it in the ResponseStorage.
-----------------------------------------------------------------------------}
function TROIndyUDPChannel.ReceiveResp(aTimeOut: Integer): Boolean;
var lBuf: String;
lID: String;
lResItm: TROResponseItem;
lResp: TStringStream;
lIP: String;
{$IFDEF RemObjects_INDY10}
lPort: TIDPort;
{$ELSE}
lPort: Integer;
{$ENDIF}
begin
FCriticalSection.Enter;
try
result := False;
lBuf := IndyClient.ReceiveString(lIP,lPort,aTimeOut);
if lBuf <> '' then
begin
lID := StripID(lBuf);
lResp := TStringStream.Create(lBuf);
lResItm := TROResponseItem.Create(lResp,lID);
lResItm.IP := lIP;
lResItm.Port := lPort;
FRespStorage.AddResp(lResItm);
result := True;
end;
finally
FCriticalSection.Leave;
end;
end;
procedure TROIndyUDPChannel.SendReq(aRequest: TStream;UID: String;AddToStorage: Boolean=true);
var lReq: String;
begin
lReq := Strm_StreamToStr(aRequest);
// Sends the Message identifier in front of the actual message
IndyClient.Send(UID+lReq);
if AddToStorage then
AddRequestToStorage(aRequest,UID);
end;
procedure TROIndyUDPChannel.SetRetrys(const Value: Integer);
begin
FRetrys := Value;
end;
function TROIndyUDPChannel.GetNewMesgID: String;
begin
result := NewGuidAsString;
end;
function TROIndyUDPChannel.GetResponseThreadClass: TROUDPResponseThreadClass;
begin
result := TROUDPResponseThread;
end;
{-----------------------------------------------------------------------------
Procedure: TROIndyUDPChannel.StripIdentifier
Author: Nico
Date: 18-apr-2003
Arguments: var aResponse: String
Result: String
Strips the leading Identifier from the given response string and returns it.
-----------------------------------------------------------------------------}
function TROIndyUDPChannel.StripID(var aResponse: String): String;
begin
if Length(aResponse) < FIDSize then
raise EROException.Create('Invalid UDP Packet received.');
result := Copy(aResponse,1,FIDSize);
Delete(aResponse,1,FIDSize);
end;
{-----------------------------------------------------------------------------
Procedure: TROIndyUDPChannel.CleanUpStorages
Author: Nico
Date: 22-apr-2003
Arguments: None
Result: None
Cleans up the Request and Response storages.
Any response without a request will be removed.
-----------------------------------------------------------------------------}
procedure TROIndyUDPChannel.CleanUpStorages;
var i: Integer;
lRespUID: String;
lReqItm: TROResponseItem;
begin
FCriticalSection.Enter;
try
// Remove respones that don't have a request present in the request-storage
for i := FRespStorage.CountOfResp-1 downto 0 do
begin
lRespUID := FRespStorage.ResponseItem[i].UID;
lReqItm := FReqStorage.GetRespByUID(lRespUID);
if lReqItm = nil then
FRespStorage.DeleteResp(i);
end;
finally
FCriticalSection.Leave;
end;
end;
function TROIndyUDPChannel.CheckStatus(const iMessageID: string): boolean;
begin
result := FRespStorage.GetRespByUID(iMessageID) <> nil;
end;
function TROIndyUDPChannel.InvokeRequest(aRequest: TStream; iGetResponse: boolean): string;
var lRT: TROUDPResponseThread;
lName: String;
begin
try
FCriticalSection.Enter;
try
result := '';
if FRunningThread <> nil then
raise EROChannelBusy.Create('Channel is busy.');
if not(Assigned(aRequest)) then
Exit;
result := GetNewMesgID;
if iGetResponse then
begin
SendReq(aRequest,result);
lName := GetResponseThreadClass.ClassName+' for reading of response %s';
lRT := GetResponseThreadClass.Create(Format(lName,[result]),Self,result);
lRT.OnTerminate := DoOnTerminateThread;
FRunningThread := lRT;
lRT.Resume;
end else
SendReq(aRequest,result,False);
finally
FCriticalSection.Leave;
end;
finally
aRequest.Free();
end;
end;
procedure TROIndyUDPChannel.RetrieveResponse(const iMessageID: string;aResponse: TStream);
var lResItm: TROResponseItem;
begin
FCriticalSection.Enter;
try
if not(Assigned(aResponse)) then
Exit;
if CheckStatus(iMessageID) then begin
lResItm := FRespStorage.GetRespByUID(iMessageID);
CopyStream(lResItm.Response,aResponse);
// Remove the response and request from the storage's
//FReqStorage.RemoveRespByUID(lResItm.UID);
FRespStorage.RemoveResp(lResItm);
end
else begin
raise EROAsyncNoAnswerYet.Create(Format(C_NORESPONSE,[iMessageID]));
end;
finally
FCriticalSection.Leave;
end;
end;
procedure TROIndyUDPChannel.DoEndOfThread(Sender: TROUDPResponseThread);
begin
CleanUpStorages;
end;
procedure TROIndyUDPChannel.DoOnTerminateThread(Sender: TObject);
begin
// Runs in the Main thread
FRunningThread := nil;
end;
procedure TROIndyUDPChannel.DoAsyncThreadTimeOut(ReqUID: String);
begin
if Assigned(FOnAsyncResponseTimeOut) then
FOnAsyncResponseTimeOut(ReqUID);
end;
procedure TROIndyUDPChannel.SetOnAsyncResponseTimeOut(const Value: TROAsyncTimeOutEvent);
begin
FOnAsyncResponseTimeOut := Value;
end;
procedure TROIndyUDPChannel.SetAsyncTimeOut(const Value: Integer);
begin
FAsyncTimeOut := Value;
if FAsyncTimeOut < 0 then
FAsyncTimeOut := 0;
end;
{ TROUDPResponseThread }
constructor TROUDPResponseThread.Create(const aName: string; aChannel: TROIndyUDPChannel; aRequestUID: String);
begin
inherited Create(True,aName);
FreeOnTerminate := true;
FRespReceived := False;
FChannel := aChannel;
FReqUID := aRequestUID;
FRetrys := aChannel.Retrys;
FCriticalSection := FChannel.FCriticalSection;
end;
procedure TROUDPResponseThread.Execute;
begin
inherited;
repeat
FChannel.ReceiveResp(FChannel.AsyncTimeOut);
Dec(FRetrys);
until (FRetrys < 1) or (FChannel.CheckStatus(FReqUID));
FRespReceived := FRetrys > 0;
if not FRespReceived
then Synchronize(DoTimeOut)
else Synchronize(DoEnd);
end;
procedure TROUDPResponseThread.DoTimeOut;
begin
FChannel.DoAsyncThreadTimeOut(FReqUID);
end;
procedure TROUDPResponseThread.DoEnd;
begin
FChannel.DoEndOfThread(Self);
end;
function TROIndyUDPChannel.GetHost: string;
begin
result := IndyClient.Host
end;
function TROIndyUDPChannel.GetPort: integer;
begin
result := IndyClient.Port
end;
procedure TROIndyUDPChannel.SetHost(const Value: string);
begin
IndyClient.Host := Value
end;
procedure TROIndyUDPChannel.SetPort(const Value: integer);
begin
IndyClient.Port := Value
end;
procedure TROIndyUDPChannel.IntSetServerLocator(
aServerLocator: TROServerLocator);
begin
Host := aServerLocator.Host;
Port := aServerLocator.Port;
end;
{$IFDEF FPC}
procedure TROIndyUDPChannel.AddResp(aResp: TROResponseItem);
begin
FRespStorage.AddResp(aResp);
end;
procedure TROIndyUDPChannel.RemoveResp(aResp: TROResponseItem);
begin
FRespStorage.RemoveResp(aResp);
end;
procedure TROIndyUDPChannel.RemoveRespByUID(aUID: String);
begin
FRespStorage.RemoveRespByUID(aUID);
end;
procedure TROIndyUDPChannel.DeleteResp(Index: Integer);
begin
FRespStorage.DeleteResp(Index);
end;
function TROIndyUDPChannel.GetResp(Index: Integer): TROResponseItem;
begin
Result:=FRespStorage.ResponseItem[Index];
end;
function TROIndyUDPChannel.GetRespByUID(aUID: String): TROResponseItem;
begin
Result:=FRespStorage.GetRespByUID(aUID);
end;
function TROIndyUDPChannel.GetResponseByUID(aUID: String): TROResponseItem;
begin
Result:=FRespStorage.GetRespByUID(aUID);
end;
function TROIndyUDPChannel.IndexOfResp(aResp: TROResponseItem): Integer;
begin
Result:=FRespStorage.IndexOfResp(aResp);
end;
function TROIndyUDPChannel.CountOfResp: Integer;
begin
Result:=FRespStorage.CountOfResp;
end;
procedure TROIndyUDPChannel.ClearStorage;
begin
FRespStorage.ClearStorage;
end;
{$ENDIF FPC}
initialization
RegisterTransportChannelClass(TROIndyUDPChannel);
finalization
UnRegisterTransportChannelClass(TROIndyUDPChannel);
end.