📄 idiohandlerstack.pas
字号:
{ $HDR$}
{**********************************************************************}
{ Unit archived using Team Coherence }
{ Team Coherence is Copyright 2002 by Quality Software Components }
{ }
{ For further information / comments, visit our WEB site at }
{ http://www.TeamCoherence.com }
{**********************************************************************}
{}
{ $Log: 11952: IdIOHandlerStack.pas
{
{ Rev 1.51 11/11/04 12:03:46 PM RLebeau
{ Updated DoConnectTimeout() to recognize IdTimeoutDefault
}
{
{ Rev 1.50 6/18/04 1:06:58 PM RLebeau
{ Bug fix for ReadTimeout property
}
{
{ Rev 1.49 5/4/2004 9:57:34 AM JPMugaas
{ Removed some old uncommented code and reenabled some TransparentProxy code
{ since it compile in DotNET.
}
{
{ Rev 1.48 2004.04.18 12:52:02 AM czhower
{ Big bug fix with server disconnect and several other bug fixed that I found
{ along the way.
}
{
{ Rev 1.47 2004.04.08 3:56:34 PM czhower
{ Fixed bug with Intercept byte count. Also removed Bytes from Buffer.
}
{
{ Rev 1.46 2004.03.12 8:01:00 PM czhower
{ Exception update
}
{
{ Rev 1.45 2004.03.07 11:48:42 AM czhower
{ Flushbuffer fix + other minor ones found
}
{
{ Rev 1.44 2004.03.01 5:12:32 PM czhower
{ -Bug fix for shutdown of servers when connections still existed (AV)
{ -Implicit HELP support in CMDserver
{ -Several command handler bugs
{ -Additional command handler functionality.
}
{
{ Rev 1.43 2/21/04 9:25:50 PM RLebeau
{ Fix for BBG #66
{
{ Added FLastSocketError member to TIdConnectThread
}
{
{ Rev 1.42 2004.02.03 4:16:48 PM czhower
{ For unit name changes.
}
{
{ Rev 1.41 12/31/2003 9:51:56 PM BGooijen
{ Added IPv6 support
}
{
{ Rev 1.40 2003.12.28 1:05:58 PM czhower
{ .Net changes.
}
{
{ Rev 1.39 11/21/2003 12:05:18 AM BGooijen
{ Terminated isn't public in TThread any more, made it public here now
}
{
{ Rev 1.38 10/28/2003 9:15:44 PM BGooijen
{ .net
}
{
{ Rev 1.37 10/18/2003 1:42:46 PM BGooijen
{ Added include
}
{
{ Rev 1.36 2003.10.14 1:26:56 PM czhower
{ Uupdates + Intercept support
}
{
{ Rev 1.35 2003.10.11 5:48:36 PM czhower
{ -VCL fixes for servers
{ -Chain suport for servers (Super core)
{ -Scheduler upgrades
{ -Full yarn support
}
{
{ Rev 1.34 10/9/2003 8:09:10 PM SPerry
{ bug fixes
}
{
{ Rev 1.33 10/5/2003 11:02:36 PM BGooijen
{ Write buffering
}
{
{ Rev 1.32 05/10/2003 23:01:02 HHariri
{ Fix for connect problem when IP address specified as opposed to host
}
{
{ Rev 1.31 2003.10.02 8:23:42 PM czhower
{ DotNet Excludes
}
{
{ Rev 1.30 2003.10.02 10:16:28 AM czhower
{ .Net
}
{
{ Rev 1.29 2003.10.01 9:11:18 PM czhower
{ .Net
}
{
{ Rev 1.28 2003.10.01 5:05:14 PM czhower
{ .Net
}
{
{ Rev 1.27 2003.10.01 2:46:38 PM czhower
{ .Net
}
{
{ Rev 1.26 2003.10.01 2:30:38 PM czhower
{ .Net
}
{
{ Rev 1.22 10/1/2003 12:14:14 AM BGooijen
{ DotNet: removing CheckForSocketError
}
{
{ Rev 1.21 2003.10.01 1:37:34 AM czhower
{ .Net
}
{
{ Rev 1.19 2003.09.30 1:22:58 PM czhower
{ Stack split for DotNet
}
{
{ Rev 1.18 2003.07.14 1:57:22 PM czhower
{ -First set of IOCP fixes.
{ -Fixed a threadsafe problem with the stack class.
}
{
{ Rev 1.17 2003.07.14 12:54:32 AM czhower
{ Fixed graceful close detection if it occurs after connect.
}
{
{ Rev 1.16 2003.07.10 4:34:58 PM czhower
{ Fixed AV, added some new comments
}
{
{ Rev 1.15 7/4/2003 08:26:46 AM JPMugaas
{ Optimizations.
}
{
{ Rev 1.14 7/1/2003 03:39:48 PM JPMugaas
{ Started numeric IP function API calls for more efficiency.
}
{
Rev 1.13 6/30/2003 10:25:18 AM BGooijen
removed unnecessary assignment to FRecvBuffer.Size
}
{
Rev 1.12 6/29/2003 10:56:28 PM BGooijen
Removed .Memory from the buffer, and added some extra methods
}
{
{ Rev 1.11 2003.06.25 4:28:32 PM czhower
{ Formatting and fixed a short circuit clause.
}
{
Rev 1.10 6/3/2003 11:43:52 PM BGooijen
Elimintated some code
}
{
Rev 1.9 4/16/2003 3:31:26 PM BGooijen
Removed InternalCheckForDisconnect, added .Connected
}
{
Rev 1.8 4/14/2003 11:44:20 AM BGooijen
CheckForDisconnect calls ReadFromSource now
}
{
Rev 1.7 4/2/2003 3:24:56 PM BGooijen
Moved transparantproxy from ..stack to ..socket
}
{
Rev 1.6 3/5/2003 11:04:32 PM BGooijen
Fixed Intercept, but the part in WriteBuffer doesn't look really nice yet
}
{
Rev 1.5 3/3/2003 11:31:58 PM BGooijen
fixed stack overflow in .CheckForDisconnect
}
{
Rev 1.4 2/26/2003 1:15:40 PM BGooijen
FBinding is now freed in IdIOHandlerSocket, instead of in IdIOHandlerStack
}
{
{ Rev 1.3 2003.02.25 1:36:12 AM czhower
}
{
{ Rev 1.2 2002.12.06 11:49:34 PM czhower
}
{
{ Rev 1.1 12-6-2002 20:10:18 BGooijen
{ Added IPv6-support
}
{
{ Rev 1.0 11/13/2002 08:45:16 AM JPMugaas
}
unit IdIOHandlerStack;
interface
uses
Classes,
IdGlobal, IdSocketHandle, IdIOHandlerSocket, IdExceptionCore, IdStack;
type
TIdIOHandlerStack = class(TIdIOHandlerSocket)
protected
procedure ConnectClient; override;
function ReadFromSource(ARaiseExceptionIfDisconnected: Boolean = True;
ATimeout: Integer = IdTimeoutDefault;
ARaiseExceptionOnTimeout: Boolean = True): Integer; override;
public
procedure CheckForDataOnSource(ATimeout: Integer = 0); override;
procedure CheckForDisconnect(ARaiseExceptionIfDisconnected: Boolean = True;
AIgnoreBuffer: Boolean = False); override;
function Connected: Boolean; override;
function Readable(AMSec: Integer = IdTimeoutDefault): Boolean; override;
// In TIdIOHandlerStack, WriteBytes must be the ONLY call to
// WriteToDestination - all data goes thru this method
procedure WriteDirect(
ABuffer: TIdBytes
); override;
published
property ReadTimeout default IdDefTimeout;
end;
implementation
uses
IdAntiFreezeBase, IdResourceStringsCore, IdResourceStrings, IdStackConsts, IdException,
IdTCPConnection, IdComponent, IdIOHandler,
SysUtils;
type
TIdConnectThread = class(TThread)
protected
FBinding: TIdSocketHandle;
FLastSocketError: Integer;
FExceptionMessage: string;
public
procedure Execute; override;
property Terminated;
end;
{ TIdIOHandlerStack }
function TIdIOHandlerStack.Connected: Boolean;
begin
ReadFromSource(False, 0, False);
Result := inherited Connected;
end;
procedure TIdIOHandlerStack.ConnectClient;
procedure DoConnectTimeout(ATimeout: Integer);
var
LSleepTime: Integer;
LInfinite: Boolean;
begin
if ATimeout = IdTimeoutDefault then begin
ATimeout := IdTimeoutInfinite;
end;
LInfinite := ATimeout = IdTimeoutInfinite;
with TIdConnectThread.Create(True) do try
FBinding := Binding;
Resume;
// Sleep
if TIdAntiFreezeBase.ShouldUse then begin
LSleepTime := Min(GAntiFreeze.IdleTimeOut, 125);
end else begin
LSleepTime := 125;
end;
if LInfinite then begin
ATimeout := LSleepTime + 1;
end;
while ATimeout > LSleepTime do begin
IdGlobal.Sleep(LSleepTime);
ATimeout := ATimeout - LSleepTime;
if LInfinite then begin
ATimeout := LSleepTime + 1;
end;
TIdAntiFreezeBase.DoProcess;
if Terminated then begin
ATimeout := 0;
Break;
end;
end;
IdGlobal.Sleep(ATimeout);
//
if Terminated then begin
if FExceptionMessage <> '' then begin
if FLastSocketError <> 0 then begin
raise EIdSocketError.CreateError(FLastSocketError, FExceptionMessage);
end;
EIdConnectException.Toss(FExceptionMessage);
end;
end else begin
Terminate;
Close;
WaitFor;
EIdConnectTimeout.Toss(RSConnectTimeout);
end;
finally Free; end;
end;
var
LHost: String;
LPort: Integer;
LIP: string;
begin
inherited;
if Assigned(FTransparentProxy) then begin
if FTransparentProxy.Enabled then begin
LHost := FTransparentProxy.Host;
LPort := FTransparentProxy.Port;
end else begin
LHost := Host;
LPort := Port;
end;
end else begin
LHost := Host;
LPort := Port;
end;
if not GStack.IsIP(LHost) then begin
if Assigned(OnStatus) then begin
DoStatus(hsResolving, [LHost]);
end;
LIP := GStack.ResolveHost(LHost, FIPVersion);
end else begin
LIP := LHost;
end;
Binding.SetPeer(LIP, LPort);
// Connect
//note for status events, we check specifically for them here
//so we don't do a string conversion in Binding.PeerIP.
if Assigned(OnStatus) then begin
DoStatus(hsConnecting, [Binding.PeerIP]);
end;
if ConnectTimeout = 0 then begin
if TIdAntiFreezeBase.ShouldUse then begin
DoConnectTimeout(120000); // 2 Min
end else begin
Binding.Connect;
end;
end else begin
DoConnectTimeout(ConnectTimeout);
end;
if Assigned(FTransparentProxy) then begin
if FTransparentProxy.Enabled then begin
FTransparentProxy.Connect(Self, Host, Port);
end;
end;
end;
function TIdIOHandlerStack.Readable(AMSec: integer): boolean;
begin
Result := Binding.Readable(AMSec);
end;
procedure TIdIOHandlerStack.WriteDirect(
ABuffer: TIdBytes
);
var
LCount: Integer;
LPos: Integer;
LSize: Integer;
begin
inherited;
EIdNotConnected.IfFalse(BindingAllocated, RSNotConnected);
LSize := Length(ABuffer);
LPos := 0;
repeat
LCount := Binding.Send(ABuffer, LPos, LSize - LPos);
// TODO - Have a AntiFreeze param which allows the send to be split up so that process
// can be called more. Maybe a prop of the connection, MaxSendSize?
TIdAntiFreezeBase.DoProcess(False);
FClosedGracefully := LCount = 0;
// Check if other side disconnected
CheckForDisconnect;
DoWork(wmWrite, LCount);
LPos := LPos + LCount;
until LPos >= LSize;
end;
function TIdIOHandlerStack.ReadFromSource(
ARaiseExceptionIfDisconnected: Boolean; ATimeout: Integer;
ARaiseExceptionOnTimeout: Boolean): Integer;
// Reads any data in tcp/ip buffer and puts it into Indy buffer
// This must be the ONLY raw read from Winsock routine
// This must be the ONLY call to RECV - all data goes thru this method
var
LByteCount: Integer;
LBuffer: TIdBytes;
begin
if ATimeout = IdTimeoutDefault then begin
if ReadTimeout = 0 then begin
ATimeout := IdTimeoutInfinite;
end else begin
ATimeout := ReadTimeout;
end;
end;
Result := 0;
// Check here as this side may have closed the socket
CheckForDisconnect(ARaiseExceptionIfDisconnected);
if BindingAllocated then begin
LByteCount := 0;
repeat
if Readable(ATimeout) then begin
if Assigned(FRecvBuffer) then begin
// No need to call AntiFreeze, the Readable does that.
if BindingAllocated then begin
// TODO: Whey are we reallocating LBuffer every time? This should
// be a one time operation per connection.
SetLength(LBuffer, RecvBufferSize); try
LByteCount := Binding.Receive(LBuffer);
SetLength(LBuffer, LByteCount);
if LByteCount > 0 then begin
if Intercept <> nil then begin
Intercept.Receive(LBuffer);
LByteCount := Length(LBuffer);
end;
//AsciiFilter - needs to go in TIdIOHandler base class
// if ASCIIFilter then begin
// for i := 1 to IOHandler.RecvBuffer.Size do begin
// PChar(IOHandler.RecvBuffer.Memory)[i] := Chr(Ord(PChar(IOHandler.RecvBuffer.Memory)[i]) and $7F);
// end;
// end;
// Pass through LBuffer first so it can go through Intercept
//TODO: If not intercept, we can skip this step
InputBuffer.Write(LBuffer);
end;
finally LBuffer := nil; end;
end else begin
EIdClosedSocket.Toss(RSStatusDisconnected);
end;
end else begin
LByteCount := 0;
EIdException.IfTrue(ARaiseExceptionIfDisconnected, RSNotConnected);
end;
FClosedGracefully := LByteCount = 0;
// Check here as other side may have closed connection
CheckForDisconnect(ARaiseExceptionIfDisconnected);
Result := LByteCount;
end else begin
// Timeout
EIdReadTimeout.IfTrue(ARaiseExceptionOnTimeout, RSReadTimeout);
Result := -1;
Break;
end;
until (LByteCount <> 0) or (BindingAllocated = False);
end else begin
if ARaiseExceptionIfDisconnected then begin
raise EIdException.Create(RSNotConnected);
end;
end;
end;
procedure TIdIOHandlerStack.CheckForDisconnect(
ARaiseExceptionIfDisconnected: Boolean; AIgnoreBuffer: Boolean);
var
LDisconnected: Boolean;
begin
// ClosedGracefully // Server disconnected
// IOHandler = nil // Client disconnected
if ClosedGracefully then begin
if BindingAllocated then begin
Close;
// Call event handlers to inform the user that we were disconnected
DoStatus(hsDisconnected);
//DoOnDisconnected;
end;
LDisconnected := True;
end else begin
LDisconnected := not BindingAllocated;
end;
// Do not raise unless all data has been read by the user
if LDisconnected then begin
if Assigned(FInputBuffer) then begin
if ((FInputBuffer.Size = 0) or AIgnoreBuffer)
and ARaiseExceptionIfDisconnected then begin
RaiseConnClosedGracefully;
end;
end;
end;
end;
procedure TIdIOHandlerStack.CheckForDataOnSource(ATimeout: Integer);
begin
if Connected then begin
ReadFromSource(False, ATimeout, False);
end;
end;
{ TIdConnectThread }
procedure TIdConnectThread.Execute;
begin
try
try
FBinding.Connect;
except
on E: EIdSocketError do begin
if (E.LastError <> Id_WSAEBADF) and (E.LastError <> Id_WSAENOTSOCK) then begin
raise;
end;
end;
end;
except
on E: Exception do begin
FExceptionMessage := E.Message;
if E is EIdSocketError then begin
FLastSocketError := EIdSocketError(E).LastError;
end;
end;
end;
// Necessary as caller checks this
Terminate;
end;
initialization
TIdIOHandlerStack.SetDefaultClass;
end.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -