| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775 |
- {
- $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
- Classes,
- IdException, IdGlobal, IdStackConsts, IdStack, IdBaseComponent;
-
- type
- TIdSocketHandle = class;
- TIdSocketHandles = class(TOwnedCollection)
- protected
- FDefaultPort: TIdPort;
- //
- function GetItem(Index: Integer): TIdSocketHandle;
- procedure SetItem(Index: Integer; const Value: TIdSocketHandle);
- public
- constructor Create(AOwner: TComponent); 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(TCollectionItem)
- protected
- FClientPortMin: TIdPort;
- FClientPortMax: TIdPort;
- FHandle: TIdStackSocketHandle;
- FHandleAllocated: Boolean;
- FIP: string;
- FPeerIP: string;
- FPort: TIdPort;
- FPeerPort: TIdPort;
- FReadSocketList: TIdSocketList;
- FSocketType : TIdSocketType;
- FOverLapped: Boolean;
- FIPVersion: TIdIPVersion;
- FConnectionHandle: TIdCriticalSection;
- FBroadcastEnabled: Boolean;
- FUseNagle : Boolean;
- FReuseSocket: TIdReuseSocket;
- //
- function BindPortReserved: Boolean;
- procedure BroadcastEnabledChanged;
- procedure SetBroadcastEnabled(const AValue: Boolean);
- procedure Disconnect; virtual;
- procedure SetBroadcastFlag(const AEnabled: Boolean);
- procedure SetOverLapped(const AValue: Boolean);
- procedure SetHandle(AHandle: TIdStackSocketHandle);
- procedure SetIPVersion(const Value: TIdIPVersion);
- procedure SetUseNagle(const AValue: Boolean);
- function TryBind(APort: TIdPort): Boolean;
- public
- function Accept(ASocket: TIdStackSocketHandle): Boolean;
- procedure AllocateSocket(const ASocketType: TIdSocketType = Id_SOCK_STREAM;
- const AProtocol: TIdSocketProtocol = Id_IPPROTO_IP);
- // Returns True if error was ignored (Matches iIgnore), false if no error occurred
- procedure Assign(Source: TPersistent); override;
- procedure Bind;
- procedure Broadcast(const AData: string; const APort: TIdPort; const AIP: String = '';
- AByteEncoding: IIdTextEncoding = nil
- {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}
- ); overload;
- procedure Broadcast(const AData: TIdBytes; const APort: TIdPort; const AIP: String = ''); overload;
- procedure CloseSocket; virtual;
- procedure Connect; virtual;
- constructor Create(ACollection: TCollection); override;
- destructor Destroy; override;
- procedure Listen(const AQueueCount: 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; var VIPVersion: TIdIPVersion): Integer;
- procedure Reset(const AResetLocal: boolean = True);
- function Send(const AData: String; AByteEncoding: IIdTextEncoding = nil
- {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}
- ): Integer; overload;
- function Send(const ABuffer: TIdBytes; const AOffset: Integer = 0; const ASize: Integer = -1): Integer; overload;
- procedure SendTo(const AIP: string; const APort: TIdPort; const AData: String;
- const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION; AByteEncoding: IIdTextEncoding = nil
- {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}
- ); overload;
- procedure SendTo(const AIP: string; const APort: TIdPort; const ABuffer : TIdBytes; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION); overload;
- procedure SendTo(const AIP: string; const APort: TIdPort; const ABuffer : TIdBytes; const AOffset: Integer; const ASize: Integer; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION); overload;
- 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;
- procedure AddMulticastMembership(const AGroupIP: String);
- procedure DropMulticastMembership(const AGroupIP: String);
- procedure SetKeepAliveValues(const AEnabled: Boolean; const ATimeMS, AInterval: Integer);
- procedure SetLoopBack(const AValue: Boolean);
- procedure SetMulticastTTL(const AValue: Byte);
- procedure SetTTL(const AValue: Integer);
- procedure SetNagleOpt(const AEnabled: Boolean);
- //
- 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;
- property SocketType : TIdSocketType read FSocketType;
- published
- property BroadcastEnabled: Boolean read FBroadcastEnabled write SetBroadcastEnabled default False;
- property ClientPortMin : TIdPort read FClientPortMin write FClientPortMin default DEF_PORT_ANY;
- property ClientPortMax : TIdPort read FClientPortMax write FClientPortMax default DEF_PORT_ANY;
- 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;
- property ReuseSocket: TIdReuseSocket read FReuseSocket write FReuseSocket default rsOSDependent;
- property UseNagle: Boolean read FUseNagle write SetUseNagle default True;
- end;
- TIdSocketHandleEvent = procedure(AHandle: TIdSocketHandle) of object;
- implementation
- uses
- {$IFDEF VCL_XE3_OR_ABOVE}
- System.SyncObjs,
- {$ENDIF}
- IdAntiFreezeBase, IdComponent, IdResourceStrings, SysUtils;
- { 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.Disconnect;
- begin
- GStack.Disconnect(Handle);
- end;
- procedure TIdSocketHandle.CloseSocket;
- begin
- FConnectionHandle.Enter;
- try
- if HandleAllocated then begin
- // 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;
- Disconnect;
- SetHandle(Id_INVALID_SOCKET);
- end;
- finally
- FConnectionHandle.Leave;
- 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? If not, call SetPeer() here...
- // SetPeer(PeerIP, PeerPort, FIPVersion);
- UpdateBindingPeer;
- end;
- finally
- FConnectionHandle.Leave;
- end;
- end;
- destructor TIdSocketHandle.Destroy;
- begin
- CloseSocket;
- FreeAndNil(FConnectionHandle);
- FreeAndNil(FReadSocketList);
- inherited Destroy;
- end;
- function TIdSocketHandle.Receive(var VBuffer: TIdBytes): Integer;
- begin
- Result := GStack.Receive(Handle, VBuffer);
- end;
- function TIdSocketHandle.Send(const AData: String; AByteEncoding: IIdTextEncoding = nil
- {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}
- ): Integer;
- begin
- Result := Send(ToBytes(AData, AByteEncoding{$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF}));
- end;
- function TIdSocketHandle.Send(const ABuffer: TIdBytes; const AOffset: Integer = 0;
- const 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);
- end;
- procedure TIdSocketHandle.SendTo(const AIP: string; const APort: TIdPort;
- const AData: String; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION;
- AByteEncoding: IIdTextEncoding = nil
- {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}
- );
- begin
- SendTo(AIP, APort, ToBytes(AData, AByteEncoding{$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF}), AIPVersion);
- end;
- procedure TIdSocketHandle.SendTo(const AIP: string; const APort: TIdPort;
- const ABuffer : TIdBytes; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION);
- begin
- SendTo(AIP, APort, ABuffer, 0, -1, AIPVersion);
- end;
- procedure TIdSocketHandle.SendTo(const AIP: string; const APort: TIdPort;
- const ABuffer : TIdBytes; const AOffset: Integer; const ASize: Integer;
- const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION);
- begin
- GStack.SendTo(Handle, ABuffer, AOffset, ASize, AIP, APort, AIPVersion);
- end;
- function TIdSocketHandle.RecvFrom(var ABuffer : TIdBytes; var VIP: string;
- var VPort: TIdPort; var VIPVersion: TIdIPVersion): Integer;
- begin
- Result := GStack.ReceiveFrom(Handle, ABuffer, VIP, VPort, VIPVersion);
- end;
- procedure TIdSocketHandle.Bind;
- var
- LValue: Integer;
- begin
- LValue := iif(
- (FReuseSocket = rsTrue) or ((FReuseSocket = rsOSDependent) and (GOSType = otUnix)),
- Id_SO_True,
- Id_SO_False
- );
- SetSockOpt(Id_SOL_SOCKET, Id_SO_REUSEADDR, LValue);
- {$IFDEF DCC}
- {$IFDEF LINUX64}
- // RLebeau 1/18/2016: Embarcadero's PAServer on Linux64 fails quickly with
- // "socket in use" errors without this option enabled. PAServer bug? For
- // now, noone else has complained about problems related to this option,
- // so let's limit this fix to just Delphi for now. Should we add a
- // HAS_SO_REUSEPORT define so FPC can use this too? What about adding a
- // new ReusePort property to configure this separately from ReuseSocket?
- // RLebeau 3/7/2017: Windows 10 has a Developer Mode that includes a Linux
- // Bash shell for running Linux executables directly in Windows. However,
- // PAServer fails to open a listening socket in this Shell with an
- // "Error #22 invalid argument" error. Since SO_REUSEPORT does not exist
- // on Windows, could that be why? Let's just ignore any socket errors here
- // for now...
- try
- SetSockOpt(Id_SOL_SOCKET, Id_SO_REUSEPORT, LValue);
- except
- on E: EIdSocketError do begin
- //if E.LastError <> EINVAL then raise;
- end;
- end;
- {$ENDIF}
- {$ENDIF}
- if (Port = 0) and (FClientPortMin <> 0) and (FClientPortMax <> 0) then begin
- if (FClientPortMin > FClientPortMax) then begin
- raise EIdInvalidPortRange.CreateFmt(RSInvalidPortRange, [FClientPortMin, FClientPortMax]);
- end else if not BindPortReserved then begin
- // TODO: skip BindPortReserved() and call GStack.Bind() directly so the
- // Exception.InnerException property can be set to report the real reason
- // why the port cannot be bound...
- raise EIdCanNotBindPortInRange.CreateFmt(RSCannotBindRange, [FClientPortMin, FClientPortMax]);
- end;
- end else {if not TryBind(Port) then} begin
- // RLebeau 1/8/2019: skipping TryBind() and calling GStack.Bind() directly so
- // the Exception.InnerException property can be set to report the real reason
- // why the port cannot be bound...
- //raise EIdCouldNotBindSocket.Create(RSCouldNotBindSocket);
- try
- GStack.Bind(Handle, FIP, Port, FIPVersion);
- UpdateBindingLocal;
- except
- IndyRaiseOuterException(EIdCouldNotBindSocket.Create(RSCouldNotBindSocket));
- end;
- end;
- end;
- procedure TIdSocketHandle.Broadcast(const AData: string; const APort: TIdPort;
- const AIP: String = ''; AByteEncoding: IIdTextEncoding = nil
- {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}
- );
- begin
- Broadcast(ToBytes(AData, AByteEncoding{$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF}), APort, AIP);
- end;
- procedure TIdSocketHandle.Broadcast(const AData: TIdBytes; const APort: TIdPort;
- const AIP: String = '');
- var
- LIP: String;
- begin
- LIP := Trim(AIP);
- if LIP = '' then begin
- if IPVersion = Id_IPv4 then begin
- // TODO: on Windows, use WSAIoctl(SIO_GET_BROADCAST_ADDRESS) instead.
- // On other platforms, use getifaddrs() or other suitable API to retreive
- // the broadcast IP if possible, or else the local IP/Subnet and then
- // calculate the broadcast IP manually...
- LIP := '255.255.255.255'; {Do not Localize}
- end else begin
- // IPv6 does not support broadcasts, multicast must be used instead...
- // TODO: make TIdStack.IPVersionUnsupported() public
- //GStack.IPVersionUnsupported;
- raise EIdIPVersionUnsupported.Create(RSIPVersionUnsupported);
- end;
- end else begin
- LIP := GStack.ResolveHost(LIP, IPVersion);
- end;
- SetBroadcastFlag(True);
- SendTo(LIP, APort, AData, IPVersion);
- BroadcastEnabledChanged;
- end;
- procedure TIdSocketHandle.BroadcastEnabledChanged;
- begin
- SetBroadcastFlag(FBroadcastEnabled);
- 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.SetBroadcastEnabled(const AValue: Boolean);
- begin
- if FBroadCastEnabled <> AValue then begin
- FBroadcastEnabled := AValue;
- if HandleAllocated then begin
- BroadcastEnabledChanged;
- end;
- end;
- end;
- procedure TIdSocketHandle.SetBroadcastFlag(const AEnabled: Boolean);
- begin
- SetSockOpt(Id_SOL_SOCKET, Id_SO_BROADCAST, iif(AEnabled, 1, 0));
- end;
- procedure TIdSocketHandle.SetOverLapped(const AValue:boolean);
- begin
- // TODO: check for HandleAllocated
- FOverLapped := AValue;
- end;
- procedure TIdSocketHandle.Listen(const AQueueCount: Integer = 5);
- begin
- GStack.Listen(Handle, AQueueCount);
- end;
- function TIdSocketHandle.Accept(ASocket: TIdStackSocketHandle): Boolean;
- var
- LAcceptedSocket: TIdStackSocketHandle;
- LIP: String;
- LPort: TIdPort;
- begin
- Reset;
- LAcceptedSocket := GStack.Accept(ASocket, LIP, LPort);
- Result := (LAcceptedSocket <> Id_INVALID_SOCKET);
- if Result then begin
- // TODO: do we need to lock FConnectionHandle here, like Connect() does?
- SetHandle(LAcceptedSocket);
- // UpdateBindingLocal is necessary as it may be listening on multiple IPs/Ports
- UpdateBindingLocal;
- //TODO: Could Peer binding ever be other than what we receive above?
- // Need to reread it? If not, use the Accept() overload that returns IPVersion
- // as well, and then call SetPeer() here...
- // SetPeer(LIP, LPort, LIPVersion);
- UpdateBindingPeer;
- end;
- end;
- constructor TIdSocketHandle.Create(ACollection: TCollection);
- begin
- inherited Create(ACollection);
- FUseNagle := True;
- FReuseSocket := rsOSDependent;
- FConnectionHandle := TIdCriticalSection.Create;
- FReadSocketList := TIdSocketList.CreateSocketList;
- Reset;
- FClientPortMin := 0;
- FClientPortMax := 0;
- FIPVersion := ID_DEFAULT_IP_VERSION;
- if Assigned(ACollection) then begin
- Port := TIdSocketHandles(ACollection).DefaultPort;
- end;
- end;
- function TIdSocketHandle.Readable(AMSec: Integer = IdTimeoutDefault): Boolean;
- function CheckIsReadable(ALMSec: Integer): Boolean;
- begin
- if not HandleAllocated then begin
- raise EIdConnClosedGracefully.Create(RSConnectionClosedGracefully);
- end;
- Result := Select(ALMSec);
- end;
- begin
- if AMSec = IdTimeoutDefault then begin
- AMSec := IdTimeoutInfinite;
- end;
- if TIdAntiFreezeBase.ShouldUse then begin
- if AMSec = IdTimeoutInfinite then begin
- repeat
- Result := CheckIsReadable(GAntiFreeze.IdleTimeOut);
- until Result;
- Exit;
- end;
- while AMSec > GAntiFreeze.IdleTimeOut do begin
- Result := CheckIsReadable(GAntiFreeze.IdleTimeOut);
- if Result then begin
- Exit;
- end;
- Dec(AMSec, GAntiFreeze.IdleTimeOut);
- end;
- end;
- Result := CheckIsReadable(AMSec);
- end;
- procedure TIdSocketHandle.Assign(Source: TPersistent);
- 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, FIPVersion);
- end;
- procedure TIdSocketHandle.UpdateBindingPeer;
- begin
- GStack.GetPeerName(Handle, FPeerIP, FPeerPort, FIPVersion);
- 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(APort: TIdPort): Boolean;
- begin
- try
- GStack.Bind(Handle, FIP, APort, 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
- if TryBind(i) 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);
- var
- LOpt: Integer;
- 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);
- GetSockOpt(Id_SOL_SOCKET, Id_SO_TYPE, FSocketType);
- //Get the NODELAY Socket option if we have a TCP Socket.
- if SocketType = Id_SOCK_STREAM then begin
- GetSockOpt(Id_SOCKETOPTIONLEVEL_TCP, Id_TCP_NODELAY, LOpt);
- FUseNagle := (LOpt = 0);
- end;
- end else begin
- FSocketType := Id_SOCK_UNKNOWN;
- 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;
- procedure TIdSocketHandle.AddMulticastMembership(const AGroupIP: String);
- begin
- GStack.AddMulticastMembership(Handle, AGroupIP, FIP, FIPVersion);
- end;
- procedure TIdSocketHandle.DropMulticastMembership(const AGroupIP: String);
- begin
- GStack.DropMulticastMembership(Handle, AGroupIP, FIP, FIPVersion);
- end;
- procedure TIdSocketHandle.SetKeepAliveValues(const AEnabled: Boolean;
- const ATimeMS, AInterval: Integer);
- begin
- GStack.SetKeepAliveValues(Handle, AEnabled, ATimeMS, AInterval);
- end;
- procedure TIdSocketHandle.SetLoopBack(const AValue: Boolean);
- begin
- GStack.SetLoopBack(Handle, AValue, FIPVersion);
- end;
- procedure TIdSocketHandle.SetMulticastTTL(const AValue: Byte);
- begin
- GStack.SetMulticastTTL(Handle, AValue, FIPVersion);
- end;
- procedure TIdSocketHandle.SetNagleOpt(const AEnabled: Boolean);
- begin
- { You only want to set a Nagle option for TCP.}
- if HandleAllocated and (SocketType = Id_SOCK_STREAM) then begin
- SetSockOpt(Id_SOCKETOPTIONLEVEL_TCP, Id_TCP_NODELAY, Integer(not AEnabled));
- end;
- end;
- procedure TIdSocketHandle.SetTTL(const AValue: Integer);
- begin
- if FIPVersion = Id_IPv4 then begin
- SetSockOpt(Id_SOL_IP, Id_SO_IP_TTL, AValue);
- end else begin
- SetSockOpt(Id_SOL_IPv6, Id_IPV6_UNICAST_HOPS, AValue);
- end;
- end;
- procedure TIdSocketHandle.SetUseNagle(const AValue: Boolean);
- begin
- if FUseNagle <> AValue then begin
- FUseNagle := AValue;
- SetNagleOpt(FUseNagle);
- 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;
- for i := Count-1 downto 0 do begin
- if Items[i].Handle = AHandle then begin
- Result := Items[i];
- Exit;
- end;
- end;
- end;
- constructor TIdSocketHandles.Create(AOwner: TComponent);
- 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.
|