www.pudn.com > indyprelim.zip > IdSocketHandle.pas
{
$Project$
$Workfile$
$Revision$
$DateUTC$
$Id$
This file is part of the Indy (Internet Direct) project, and is offered
under the dual-licensing agreement described on the Indy website.
(http://www.indyproject.org/)
Copyright:
(c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved.
}
{
$Log$
}
{
Rev 1.8 4/11/2005 2:17:46 PM JPMugaas
Fix from Ben Taylor for where a pointer is used after it's freed causing an
invalid pointer operation.
Rev 1.7 23.3.2005 ã. 20:50:04 DBondzhev
Fixed problem on multi CPU systems when connection is closed while it get's
connected at the end of the timeout period.
Rev 1.6 11/15/2004 11:40:08 PM JPMugaas
Added IPAddressType parameter to SetBinding )AIPVersion). This would set the
same variable as the SetPeer AIPVersion parameter. It's just a convenience
sake since both the receiver and sender must have the same type of IP address
(unless there's a gateway thing we support).
Rev 1.5 11/12/2004 11:30:18 AM JPMugaas
Expansions for IPv6.
Rev 1.4 09/06/2004 09:48:42 CCostelloe
Kylix 3 patch
Rev 1.3 4/26/04 12:40:26 PM RLebeau
Removed recursion from Readable()
Rev 1.2 2004.03.07 11:48:48 AM czhower
Flushbuffer fix + other minor ones found
Rev 1.1 3/6/2004 5:16:14 PM JPMugaas
Bug 67 fixes. Do not write to const values.
Rev 1.0 2004.02.03 3:14:40 PM czhower
Move and updates
Rev 1.23 2/2/2004 12:09:16 AM JPMugaas
GetSockOpt should now work in DotNET.
Rev 1.22 2/1/2004 6:10:46 PM JPMugaas
GetSockOpt.
Rev 1.21 12/31/2003 9:51:58 PM BGooijen
Added IPv6 support
Rev 1.20 10/26/2003 12:29:40 PM BGooijen
DotNet
Rev 1.19 10/22/2003 04:40:48 PM JPMugaas
Should compile with some restored functionality. Still not finished.
Rev 1.18 2003.10.11 5:50:26 PM czhower
-VCL fixes for servers
-Chain suport for servers (Super core)
-Scheduler upgrades
-Full yarn support
Rev 1.17 10/5/2003 9:55:30 PM BGooijen
TIdTCPServer works on D7 and DotNet now
Rev 1.16 2003.10.02 12:44:42 PM czhower
Fix for Bind, Connect
Rev 1.15 2003.10.02 10:16:28 AM czhower
.Net
Rev 1.14 2003.10.01 9:11:20 PM czhower
.Net
Rev 1.13 2003.10.01 5:05:14 PM czhower
.Net
Rev 1.12 2003.10.01 2:30:40 PM czhower
.Net
Rev 1.10 10/1/2003 12:14:12 AM BGooijen
DotNet: removing CheckForSocketError
Rev 1.9 2003.10.01 1:12:36 AM czhower
.Net
Rev 1.8 2003.09.30 1:23:02 PM czhower
Stack split for DotNet
Rev 1.7 20.09.2003 16:33:28 ARybin
bug fix:
NOT Integer <> 0 is not boolean operation, because:
(NOT Integer) = inverted integer
Rev 1.6 2003.07.14 1:57:24 PM czhower
-First set of IOCP fixes.
-Fixed a threadsafe problem with the stack class.
Rev 1.5 7/1/2003 05:20:36 PM JPMugaas
Minor optimizations. Illiminated some unnecessary string operations.
Rev 1.4 7/1/2003 03:39:52 PM JPMugaas
Started numeric IP function API calls for more efficiency.
Rev 1.3 5/11/2003 11:59:06 AM BGooijen
Added OverLapped property
Rev 1.2 5/11/2003 12:35:30 AM BGooijen
temporary creates overlapped socked handles
Rev 1.1 3/21/2003 01:50:08 AM JPMugaas
SetBinding method added as per request received in private E-Mail.
Rev 1.0 11/13/2002 08:58:46 AM JPMugaas
}
unit IdSocketHandle;
interface
{$I IdCompilerDefines.inc}
uses
IdException, IdGlobal, IdStackConsts, IdStack, IdSys, IdObjs, IdBaseComponent;
type
TIdSocketHandle = class;
TIdSocketHandles = class(TIdOwnedCollection)
protected
FDefaultPort: TIdPort;
//
function GetItem(Index: Integer): TIdSocketHandle;
procedure SetItem(Index: Integer; const Value: TIdSocketHandle);
public
constructor Create(AOwner: TIdNativeComponent); reintroduce;
function Add: TIdSocketHandle; reintroduce;
function BindingByHandle(const AHandle: TIdStackSocketHandle): TIdSocketHandle;
property Items[Index: Integer]: TIdSocketHandle read GetItem write SetItem; default;
//
property DefaultPort: TIdPort read FDefaultPort write FDefaultPort;
end;
TIdSocketHandle = class(TIdCollectionItem)
protected
FClientPortMin: TIdPort;
FClientPortMax: TIdPort;
FHandle: TIdStackSocketHandle;
FHandleAllocated: Boolean;
FIP: string;
FPeerIP: string;
FPort: TIdPort;
FPeerPort: TIdPort;
FReadSocketList: TIdSocketList;
FOverLapped: Boolean;
FIPVersion: TIdIPVersion;
FConnectionHandle: TIdCriticalSection;
//
function BindPortReserved: Boolean;
procedure SetOverLapped(const AValue:boolean);
procedure SetHandle(AHandle: TIdStackSocketHandle);
procedure SetIPVersion(const Value: TIdIPVersion);
function TryBind: Boolean;
public
function Accept(ASocket: TIdStackSocketHandle): Boolean;
{$IFDEF LINUX}
procedure AllocateSocket(const ASocketType: TIdSocketType = TIdSocketType(Id_SOCK_STREAM);
const AProtocol: TIdSocketProtocol = Id_IPPROTO_IP);
{$ELSE}
procedure AllocateSocket(const ASocketType: TIdSocketType = Id_SOCK_STREAM;
const AProtocol: TIdSocketProtocol = Id_IPPROTO_IP);
{$ENDIF}
// Returns True if error was ignored (Matches iIgnore), false if no error occurred
procedure Assign(Source: TIdPersistent); override;
procedure Bind;
procedure CloseSocket; virtual;
procedure Connect; virtual;
constructor Create(ACollection: TIdCollection); override;
destructor Destroy; override;
// procedure GetSockOpt(level, optname: Integer; optval: PChar; optlen: Integer);
procedure Listen(const anQueueCount: integer = 5);
function Readable(AMSec: Integer = IdTimeoutDefault): boolean;
function Receive(var VBuffer: TIdBytes): Integer;
function RecvFrom(var ABuffer : TIdBytes; var VIP: string;
var VPort: TIdPort; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION): Integer;
procedure Reset(const AResetLocal: boolean = True);
function Send(
const ABuffer: TIdBytes;
AOffset: Integer;
ASize: Integer = -1
): Integer;
procedure SendTo(const AIP: string; const APort: TIdPort; const ABuffer : TIdBytes; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION);
procedure SetPeer(const AIP: string; const APort: TIdPort; const AIPVersion : TIdIPVersion = ID_DEFAULT_IP_VERSION);
procedure SetBinding(const AIP: string; const APort: TIdPort; const AIPVersion : TIdIPVersion = ID_DEFAULT_IP_VERSION);
procedure GetSockOpt(ALevel:TIdSocketOptionLevel; AOptName: TIdSocketOption; out VOptVal: Integer);
procedure SetSockOpt(ALevel:TIdSocketOptionLevel; AOptName: TIdSocketOption; AOptVal: Integer);
function Select(ATimeout: Integer = IdTimeoutInfinite): Boolean;
procedure UpdateBindingLocal;
procedure UpdateBindingPeer;
//
property HandleAllocated: Boolean read FHandleAllocated;
property Handle: TIdStackSocketHandle read FHandle;
property OverLapped:boolean read FOverLapped write SetOverLapped;
property PeerIP: string read FPeerIP;
property PeerPort: TIdPort read FPeerPort;
published
property ClientPortMin : TIdPort read FClientPortMin write FClientPortMin default 0;
property ClientPortMax : TIdPort read FClientPortMax write FClientPortMax default 0;
property IP: string read FIP write FIP;
property IPVersion: TIdIPVersion read FIPVersion write SetIPVersion default ID_DEFAULT_IP_VERSION;
property Port: TIdPort read FPort write FPort;
end;
implementation
uses
IdAntiFreezeBase, IdComponent, IdResourceStrings;
{ TIdSocketHandle }
procedure TIdSocketHandle.AllocateSocket(
const ASocketType: TIdSocketType;
const AProtocol: TIdSocketProtocol);
begin
// If we are reallocating a socket - close and destroy the old socket handle
CloseSocket;
if HandleAllocated then begin
Reset;
end;
// Set property so it calls the writer
SetHandle(GStack.NewSocketHandle(ASocketType, AProtocol, FIPVersion, FOverLapped));
end;
procedure TIdSocketHandle.CloseSocket;
begin
if HandleAllocated then begin
FConnectionHandle.Enter; try
// Must be first, closing socket will trigger some errors, and they
// may then call (in other threads) Connected, which in turn looks at
// FHandleAllocated.
FHandleAllocated := False;
GStack.Disconnect(Handle);
SetHandle(Id_INVALID_SOCKET);
finally
FConnectionHandle.Leave;
end;
end;
end;
procedure TIdSocketHandle.Connect;
begin
GStack.Connect(Handle, PeerIP, PeerPort, FIPVersion);
FConnectionHandle.Enter; try
if (HandleAllocated) then begin
// UpdateBindingLocal needs to be called even though Bind calls it. After
// Bind is may be 0.0.0.0 (INADDR_ANY). After connect it will be a real IP.
UpdateBindingLocal;
//TODO: Could Peer binding ever be other than what we specified above?
// Need to reread it?
UpdateBindingPeer;
end;
finally
FConnectionHandle.Leave;
end;
end;
destructor TIdSocketHandle.Destroy;
begin
CloseSocket;
Sys.FreeAndNil(FConnectionHandle);
Sys.FreeAndNil(FReadSocketList);
inherited Destroy;
end;
function TIdSocketHandle.Receive(var VBuffer: TIdBytes): Integer;
begin
Result := GStack.Receive(Handle, VBuffer);
end;
function TIdSocketHandle.Send(
const ABuffer: TIdBytes;
AOffset: Integer;
ASize: Integer = -1
): Integer;
begin
Result := GStack.Send(Handle, ABuffer, AOffset, ASize);
end;
procedure TIdSocketHandle.SetSockOpt(ALevel:TIdSocketOptionLevel;
AOptName: TIdSocketOption; AOptVal: Integer);
begin
GStack.SetSocketOption(Handle,ALevel,AOptName,AOptVal);
//// (GStack as TIdStackBSDBase).WSSetSockOpt(Handle, level, optname, optval, optlen);
end;
procedure TIdSocketHandle.SendTo(const AIP: string; const APort: TIdPort; const ABuffer : TIdBytes; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION);
begin
GStack.SendTo(Handle, ABuffer, 0, AIP, APort,AIPVersion);
end;
function TIdSocketHandle.RecvFrom(var ABuffer : TIdBytes; var VIP: string;
var VPort: TIdPort; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION): Integer;
begin
Result := GStack.ReceiveFrom(Handle,ABuffer,VIP,VPort,AIPVersion);
end;
procedure TIdSocketHandle.Bind;
begin
if (Port = 0) and (FClientPortMin <> 0) and (FClientPortMax <> 0) then begin
if (FClientPortMin > FClientPortMax) then begin
raise EIdInvalidPortRange.Create(Sys.Format(RSInvalidPortRange
, [FClientPortMin, FClientPortMax]));
end else if not BindPortReserved then begin
raise EIdCanNotBindPortInRange.Create(Sys.Format(RSCanNotBindRange
, [FClientPortMin, FClientPortMax]));
end;
end else if not TryBind then begin
raise EIdCouldNotBindSocket.Create(RSCouldNotBindSocket);
end;
end;
procedure TIdSocketHandle.SetPeer(const AIP: string; const APort: TIdPort; const AIPVersion : TIdIPVersion = ID_DEFAULT_IP_VERSION);
begin
FPeerIP := AIP;
FPeerPort := APort;
FIPVersion := AIPVersion;
end;
procedure TIdSocketHandle.SetBinding(const AIP: string; const APort: TIdPort; const AIPVersion : TIdIPVersion = ID_DEFAULT_IP_VERSION);
begin
FIP := AIP;
FPort := APort;
FIPVersion := AIPVersion;
end;
procedure TIdSocketHandle.SetOverLapped(const AValue:boolean);
begin
// TODO: check for HandleAllocated
FOverLapped := AValue;
end;
procedure TIdSocketHandle.Listen(const anQueueCount: integer);
begin
GStack.Listen(Handle, anQueueCount);
end;
function TIdSocketHandle.Accept(ASocket: TIdStackSocketHandle): Boolean;
var
LAcceptedSocket: TIdStackSocketHandle;
begin
Reset;
LAcceptedSocket := GStack.Accept(ASocket, FIP, FPort, FIPVersion);
Result := (LAcceptedSocket <> Id_INVALID_SOCKET);
if Result then begin
SetHandle(LAcceptedSocket);
// UpdateBindingLocal is necessary as it may be listening on multiple IPs/Ports
UpdateBindingLocal;
UpdateBindingPeer;
end;
end;
constructor TIdSocketHandle.Create(ACollection: TIdCollection);
begin
inherited Create(ACollection);
FConnectionHandle := TIdCriticalSection.Create;
FReadSocketList := TIdSocketList.CreateSocketList;
Reset;
FClientPortMin := 0;
FClientPortMax := 0;
if Assigned(ACollection) then begin
Port := TIdSocketHandles(ACollection).DefaultPort;
end;
end;
function TIdSocketHandle.Readable(AMSec: Integer = IdTimeoutDefault): Boolean;
function CheckIsReadable(AMSec: Integer): Boolean;
begin
if HandleAllocated then begin
Result := Select(AMSec);
end else begin
raise EIdConnClosedGracefully.Create(RSConnectionClosedGracefully);
end;
end;
begin
if TIdAntiFreezeBase.ShouldUse then begin
if AMSec = IdTimeoutInfinite then begin
repeat
Result := CheckIsReadable(GAntiFreeze.IdleTimeOut);
until Result;
Exit;
end else if AMSec > GAntiFreeze.IdleTimeOut then begin
Result := CheckIsReadable(AMSec - GAntiFreeze.IdleTimeOut);
if Result then begin
Exit;
end;
AMSec := GAntiFreeze.IdleTimeOut;
end;
end;
Result := CheckIsReadable(AMSec);
end;
procedure TIdSocketHandle.Assign(Source: TIdPersistent);
var
LSource: TIdSocketHandle;
begin
if Source is TIdSocketHandle then begin
LSource := TIdSocketHandle(Source);
FIP := LSource.FIP;
Port := LSource.Port;
FPeerIP := LSource.FPeerIP;
FPeerPort := LSource.FPeerPort;
FIPVersion := LSource.IPVersion;
end else begin
inherited
end;
end;
procedure TIdSocketHandle.UpdateBindingLocal;
begin
GStack.GetSocketName(Handle, FIP, FPort);
end;
procedure TIdSocketHandle.UpdateBindingPeer;
begin
GStack.GetPeerName(Handle, FPeerIP, FPeerPort);
end;
procedure TIdSocketHandle.Reset(const AResetLocal: boolean = True);
begin
SetHandle(Id_INVALID_SOCKET);
if AResetLocal then begin
FIP := '';
FPort := 0;
end;
FPeerIP := '';
FPeerPort := 0;
FIPVersion := ID_DEFAULT_IP_VERSION;
end;
function TIdSocketHandle.TryBind: Boolean;
begin
try
GStack.Bind(Handle, FIP, Port, FIPVersion);
Result := True;
UpdateBindingLocal;
except
Result := False;
end;
end;
function TIdSocketHandle.BindPortReserved: Boolean;
var
i : TIdPort;
begin
Result := false;
for i := FClientPortMax downto FClientPortMin do begin
FPort := i;
if TryBind then begin
Result := True;
Exit;
end;
end;
end;
procedure TIdSocketHandle.GetSockOpt(ALevel:TIdSocketOptionLevel; AOptName: TIdSocketOption; out VOptVal: Integer);
begin
GStack.GetSocketOption(Handle,ALevel,AOptName,VOptVal);
end;
function TIdSocketHandle.Select(ATimeOut: Integer = IdTimeoutInfinite): Boolean;
begin
Result := FReadSocketList.SelectRead(ATimeOut);
TIdAntiFreezeBase.DoProcess(Result = False);
end;
procedure TIdSocketHandle.SetHandle(AHandle: TIdStackSocketHandle);
begin
if FHandle <> Id_INVALID_SOCKET then begin
FReadSocketList.Remove(FHandle);
end;
FHandle := AHandle;
FHandleAllocated := FHandle <> Id_INVALID_SOCKET;
if FHandleAllocated then begin
FReadSocketList.Add(FHandle);
end;
end;
procedure TIdSocketHandle.SetIPVersion(const Value: TIdIPVersion);
begin
if Value <> FIPVersion then begin
if HandleAllocated then begin
raise EIdCannotSetIPVersionWhenConnected.Create(RSCannotSetIPVersionWhenConnected);
end;
FIPVersion := Value;
end;
end;
{ TIdSocketHandles }
function TIdSocketHandles.Add: TIdSocketHandle;
begin
Result := inherited Add as TIdSocketHandle;
Result.Port := DefaultPort;
end;
function TIdSocketHandles.BindingByHandle(const AHandle: TIdStackSocketHandle): TIdSocketHandle;
var
i: integer;
begin
Result := nil;
i := Count - 1;
while (i >= 0) and (Items[i].Handle <> AHandle) do begin
dec(i);
end;
if i >= 0 then begin
Result := Items[i];
end;
end;
constructor TIdSocketHandles.Create(AOwner: TIdNativeComponent);
begin
inherited Create(AOwner, TIdSocketHandle);
end;
function TIdSocketHandles.GetItem(Index: Integer): TIdSocketHandle;
begin
Result := TIdSocketHandle(inherited Items[index]);
end;
procedure TIdSocketHandles.SetItem(Index: Integer; const Value: TIdSocketHandle);
begin
inherited SetItem(Index, Value);
end;
end.