IdSocketHandle.pas 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775
  1. {
  2. $Project$
  3. $Workfile$
  4. $Revision$
  5. $DateUTC$
  6. $Id$
  7. This file is part of the Indy (Internet Direct) project, and is offered
  8. under the dual-licensing agreement described on the Indy website.
  9. (http://www.indyproject.org/)
  10. Copyright:
  11. (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved.
  12. }
  13. {
  14. $Log$
  15. }
  16. {
  17. Rev 1.8 4/11/2005 2:17:46 PM JPMugaas
  18. Fix from Ben Taylor for where a pointer is used after it's freed causing an
  19. invalid pointer operation.
  20. Rev 1.7 23.3.2005 ã. 20:50:04 DBondzhev
  21. Fixed problem on multi CPU systems when connection is closed while it get's
  22. connected at the end of the timeout period.
  23. Rev 1.6 11/15/2004 11:40:08 PM JPMugaas
  24. Added IPAddressType parameter to SetBinding )AIPVersion). This would set the
  25. same variable as the SetPeer AIPVersion parameter. It's just a convenience
  26. sake since both the receiver and sender must have the same type of IP address
  27. (unless there's a gateway thing we support).
  28. Rev 1.5 11/12/2004 11:30:18 AM JPMugaas
  29. Expansions for IPv6.
  30. Rev 1.4 09/06/2004 09:48:42 CCostelloe
  31. Kylix 3 patch
  32. Rev 1.3 4/26/04 12:40:26 PM RLebeau
  33. Removed recursion from Readable()
  34. Rev 1.2 2004.03.07 11:48:48 AM czhower
  35. Flushbuffer fix + other minor ones found
  36. Rev 1.1 3/6/2004 5:16:14 PM JPMugaas
  37. Bug 67 fixes. Do not write to const values.
  38. Rev 1.0 2004.02.03 3:14:40 PM czhower
  39. Move and updates
  40. Rev 1.23 2/2/2004 12:09:16 AM JPMugaas
  41. GetSockOpt should now work in DotNET.
  42. Rev 1.22 2/1/2004 6:10:46 PM JPMugaas
  43. GetSockOpt.
  44. Rev 1.21 12/31/2003 9:51:58 PM BGooijen
  45. Added IPv6 support
  46. Rev 1.20 10/26/2003 12:29:40 PM BGooijen
  47. DotNet
  48. Rev 1.19 10/22/2003 04:40:48 PM JPMugaas
  49. Should compile with some restored functionality. Still not finished.
  50. Rev 1.18 2003.10.11 5:50:26 PM czhower
  51. -VCL fixes for servers
  52. -Chain suport for servers (Super core)
  53. -Scheduler upgrades
  54. -Full yarn support
  55. Rev 1.17 10/5/2003 9:55:30 PM BGooijen
  56. TIdTCPServer works on D7 and DotNet now
  57. Rev 1.16 2003.10.02 12:44:42 PM czhower
  58. Fix for Bind, Connect
  59. Rev 1.15 2003.10.02 10:16:28 AM czhower
  60. .Net
  61. Rev 1.14 2003.10.01 9:11:20 PM czhower
  62. .Net
  63. Rev 1.13 2003.10.01 5:05:14 PM czhower
  64. .Net
  65. Rev 1.12 2003.10.01 2:30:40 PM czhower
  66. .Net
  67. Rev 1.10 10/1/2003 12:14:12 AM BGooijen
  68. DotNet: removing CheckForSocketError
  69. Rev 1.9 2003.10.01 1:12:36 AM czhower
  70. .Net
  71. Rev 1.8 2003.09.30 1:23:02 PM czhower
  72. Stack split for DotNet
  73. Rev 1.7 20.09.2003 16:33:28 ARybin
  74. bug fix:
  75. NOT Integer <> 0 is not boolean operation, because:
  76. (NOT Integer) = inverted integer
  77. Rev 1.6 2003.07.14 1:57:24 PM czhower
  78. -First set of IOCP fixes.
  79. -Fixed a threadsafe problem with the stack class.
  80. Rev 1.5 7/1/2003 05:20:36 PM JPMugaas
  81. Minor optimizations. Illiminated some unnecessary string operations.
  82. Rev 1.4 7/1/2003 03:39:52 PM JPMugaas
  83. Started numeric IP function API calls for more efficiency.
  84. Rev 1.3 5/11/2003 11:59:06 AM BGooijen
  85. Added OverLapped property
  86. Rev 1.2 5/11/2003 12:35:30 AM BGooijen
  87. temporary creates overlapped socked handles
  88. Rev 1.1 3/21/2003 01:50:08 AM JPMugaas
  89. SetBinding method added as per request received in private E-Mail.
  90. Rev 1.0 11/13/2002 08:58:46 AM JPMugaas
  91. }
  92. unit IdSocketHandle;
  93. interface
  94. {$I IdCompilerDefines.inc}
  95. uses
  96. Classes,
  97. IdException, IdGlobal, IdStackConsts, IdStack;
  98. type
  99. TIdSocketHandle = class;
  100. TIdSocketHandles = class(TOwnedCollection)
  101. protected
  102. FDefaultPort: TIdPort;
  103. //
  104. function GetItem(Index: Integer): TIdSocketHandle;
  105. procedure SetItem(Index: Integer; const Value: TIdSocketHandle);
  106. public
  107. constructor Create(AOwner: TComponent); reintroduce;
  108. function Add: TIdSocketHandle; reintroduce;
  109. function BindingByHandle(const AHandle: TIdStackSocketHandle): TIdSocketHandle;
  110. property Items[Index: Integer]: TIdSocketHandle read GetItem write SetItem; default;
  111. //
  112. property DefaultPort: TIdPort read FDefaultPort write FDefaultPort;
  113. end;
  114. TIdSocketHandle = class(TCollectionItem)
  115. protected
  116. FClientPortMin: TIdPort;
  117. FClientPortMax: TIdPort;
  118. FHandle: TIdStackSocketHandle;
  119. FHandleAllocated: Boolean;
  120. FIP: string;
  121. FPeerIP: string;
  122. FPort: TIdPort;
  123. FPeerPort: TIdPort;
  124. FReadSocketList: TIdSocketList;
  125. FSocketType : TIdSocketType;
  126. FOverLapped: Boolean;
  127. FIPVersion: TIdIPVersion;
  128. FConnectionHandle: TIdCriticalSection;
  129. FBroadcastEnabled: Boolean;
  130. FUseNagle : Boolean;
  131. FReuseSocket: TIdReuseSocket;
  132. //
  133. function BindPortReserved: Boolean;
  134. procedure BroadcastEnabledChanged;
  135. procedure SetBroadcastEnabled(const AValue: Boolean);
  136. procedure Disconnect; virtual;
  137. procedure SetBroadcastFlag(const AEnabled: Boolean);
  138. procedure SetOverLapped(const AValue: Boolean);
  139. procedure SetHandle(AHandle: TIdStackSocketHandle);
  140. procedure SetIPVersion(const Value: TIdIPVersion);
  141. procedure SetUseNagle(const AValue: Boolean);
  142. function TryBind(APort: TIdPort): Boolean;
  143. public
  144. function Accept(ASocket: TIdStackSocketHandle): Boolean;
  145. procedure AllocateSocket(const ASocketType: TIdSocketType = Id_SOCK_STREAM;
  146. const AProtocol: TIdSocketProtocol = Id_IPPROTO_IP);
  147. // Returns True if error was ignored (Matches iIgnore), false if no error occurred
  148. procedure Assign(Source: TPersistent); override;
  149. procedure Bind;
  150. procedure Broadcast(const AData: string; const APort: TIdPort; const AIP: String = '';
  151. AByteEncoding: IIdTextEncoding = nil
  152. {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}
  153. ); overload;
  154. procedure Broadcast(const AData: TIdBytes; const APort: TIdPort; const AIP: String = ''); overload;
  155. procedure CloseSocket; virtual;
  156. procedure Connect; virtual;
  157. constructor Create(ACollection: TCollection); override;
  158. destructor Destroy; override;
  159. procedure Listen(const AQueueCount: Integer = 5);
  160. function Readable(AMSec: Integer = IdTimeoutDefault): boolean;
  161. function Receive(var VBuffer: TIdBytes): Integer;
  162. function RecvFrom(var ABuffer : TIdBytes; var VIP: string;
  163. var VPort: TIdPort; var VIPVersion: TIdIPVersion): Integer;
  164. procedure Reset(const AResetLocal: boolean = True);
  165. function Send(const AData: String; AByteEncoding: IIdTextEncoding = nil
  166. {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}
  167. ): Integer; overload;
  168. function Send(const ABuffer: TIdBytes; const AOffset: Integer = 0; const ASize: Integer = -1): Integer; overload;
  169. procedure SendTo(const AIP: string; const APort: TIdPort; const AData: String;
  170. const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION; AByteEncoding: IIdTextEncoding = nil
  171. {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}
  172. ); overload;
  173. procedure SendTo(const AIP: string; const APort: TIdPort; const ABuffer : TIdBytes; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION); overload;
  174. 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;
  175. procedure SetPeer(const AIP: string; const APort: TIdPort; const AIPVersion : TIdIPVersion = ID_DEFAULT_IP_VERSION);
  176. procedure SetBinding(const AIP: string; const APort: TIdPort; const AIPVersion : TIdIPVersion = ID_DEFAULT_IP_VERSION);
  177. procedure GetSockOpt(ALevel: TIdSocketOptionLevel; AOptName: TIdSocketOption; out VOptVal: Integer);
  178. procedure SetSockOpt(ALevel: TIdSocketOptionLevel; AOptName: TIdSocketOption; AOptVal: Integer);
  179. function Select(ATimeout: Integer = IdTimeoutInfinite): Boolean;
  180. procedure UpdateBindingLocal;
  181. procedure UpdateBindingPeer;
  182. procedure AddMulticastMembership(const AGroupIP: String);
  183. procedure DropMulticastMembership(const AGroupIP: String);
  184. procedure SetKeepAliveValues(const AEnabled: Boolean; const ATimeMS, AInterval: Integer);
  185. procedure SetLoopBack(const AValue: Boolean);
  186. procedure SetMulticastTTL(const AValue: Byte);
  187. procedure SetTTL(const AValue: Integer);
  188. procedure SetNagleOpt(const AEnabled: Boolean);
  189. //
  190. property HandleAllocated: Boolean read FHandleAllocated;
  191. property Handle: TIdStackSocketHandle read FHandle;
  192. property OverLapped: Boolean read FOverLapped write SetOverLapped;
  193. property PeerIP: string read FPeerIP;
  194. property PeerPort: TIdPort read FPeerPort;
  195. property SocketType : TIdSocketType read FSocketType;
  196. published
  197. property BroadcastEnabled: Boolean read FBroadcastEnabled write SetBroadcastEnabled default False;
  198. property ClientPortMin : TIdPort read FClientPortMin write FClientPortMin default DEF_PORT_ANY;
  199. property ClientPortMax : TIdPort read FClientPortMax write FClientPortMax default DEF_PORT_ANY;
  200. property IP: string read FIP write FIP;
  201. property IPVersion: TIdIPVersion read FIPVersion write SetIPVersion default ID_DEFAULT_IP_VERSION;
  202. property Port: TIdPort read FPort write FPort;
  203. property ReuseSocket: TIdReuseSocket read FReuseSocket write FReuseSocket default rsOSDependent;
  204. property UseNagle: Boolean read FUseNagle write SetUseNagle default True;
  205. end;
  206. TIdSocketHandleEvent = procedure(AHandle: TIdSocketHandle) of object;
  207. implementation
  208. uses
  209. {$IFDEF VCL_XE3_OR_ABOVE}
  210. System.SyncObjs,
  211. {$ENDIF}
  212. IdAntiFreezeBase, IdComponent, IdResourceStrings, SysUtils;
  213. { TIdSocketHandle }
  214. procedure TIdSocketHandle.AllocateSocket(const ASocketType: TIdSocketType;
  215. const AProtocol: TIdSocketProtocol);
  216. begin
  217. // If we are reallocating a socket - close and destroy the old socket handle
  218. CloseSocket;
  219. if HandleAllocated then begin
  220. Reset;
  221. end;
  222. // Set property so it calls the writer
  223. SetHandle(GStack.NewSocketHandle(ASocketType, AProtocol, FIPVersion, FOverLapped));
  224. end;
  225. procedure TIdSocketHandle.Disconnect;
  226. begin
  227. GStack.Disconnect(Handle);
  228. end;
  229. procedure TIdSocketHandle.CloseSocket;
  230. begin
  231. FConnectionHandle.Enter;
  232. try
  233. if HandleAllocated then begin
  234. // Must be first, closing socket will trigger some errors, and they
  235. // may then call (in other threads) Connected, which in turn looks at
  236. // FHandleAllocated.
  237. FHandleAllocated := False;
  238. Disconnect;
  239. SetHandle(Id_INVALID_SOCKET);
  240. end;
  241. finally
  242. FConnectionHandle.Leave;
  243. end;
  244. end;
  245. procedure TIdSocketHandle.Connect;
  246. begin
  247. GStack.Connect(Handle, PeerIP, PeerPort, FIPVersion);
  248. FConnectionHandle.Enter;
  249. try
  250. if HandleAllocated then begin
  251. // UpdateBindingLocal needs to be called even though Bind calls it. After
  252. // Bind is may be 0.0.0.0 (INADDR_ANY). After connect it will be a real IP.
  253. UpdateBindingLocal;
  254. //TODO: Could Peer binding ever be other than what we specified above?
  255. // Need to reread it? If not, call SetPeer() here...
  256. // SetPeer(PeerIP, PeerPort, FIPVersion);
  257. UpdateBindingPeer;
  258. end;
  259. finally
  260. FConnectionHandle.Leave;
  261. end;
  262. end;
  263. destructor TIdSocketHandle.Destroy;
  264. begin
  265. CloseSocket;
  266. FreeAndNil(FConnectionHandle);
  267. FreeAndNil(FReadSocketList);
  268. inherited Destroy;
  269. end;
  270. function TIdSocketHandle.Receive(var VBuffer: TIdBytes): Integer;
  271. begin
  272. Result := GStack.Receive(Handle, VBuffer);
  273. end;
  274. function TIdSocketHandle.Send(const AData: String; AByteEncoding: IIdTextEncoding = nil
  275. {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}
  276. ): Integer;
  277. begin
  278. Result := Send(ToBytes(AData, AByteEncoding{$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF}));
  279. end;
  280. function TIdSocketHandle.Send(const ABuffer: TIdBytes; const AOffset: Integer = 0;
  281. const ASize: Integer = -1): Integer;
  282. begin
  283. Result := GStack.Send(Handle, ABuffer, AOffset, ASize);
  284. end;
  285. procedure TIdSocketHandle.SetSockOpt(ALevel: TIdSocketOptionLevel;
  286. AOptName: TIdSocketOption; AOptVal: Integer);
  287. begin
  288. GStack.SetSocketOption(Handle, ALevel, AOptName, AOptVal);
  289. end;
  290. procedure TIdSocketHandle.SendTo(const AIP: string; const APort: TIdPort;
  291. const AData: String; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION;
  292. AByteEncoding: IIdTextEncoding = nil
  293. {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}
  294. );
  295. begin
  296. SendTo(AIP, APort, ToBytes(AData, AByteEncoding{$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF}), AIPVersion);
  297. end;
  298. procedure TIdSocketHandle.SendTo(const AIP: string; const APort: TIdPort;
  299. const ABuffer : TIdBytes; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION);
  300. begin
  301. SendTo(AIP, APort, ABuffer, 0, -1, AIPVersion);
  302. end;
  303. procedure TIdSocketHandle.SendTo(const AIP: string; const APort: TIdPort;
  304. const ABuffer : TIdBytes; const AOffset: Integer; const ASize: Integer;
  305. const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION);
  306. begin
  307. GStack.SendTo(Handle, ABuffer, AOffset, ASize, AIP, APort, AIPVersion);
  308. end;
  309. function TIdSocketHandle.RecvFrom(var ABuffer : TIdBytes; var VIP: string;
  310. var VPort: TIdPort; var VIPVersion: TIdIPVersion): Integer;
  311. begin
  312. Result := GStack.ReceiveFrom(Handle, ABuffer, VIP, VPort, VIPVersion);
  313. end;
  314. procedure TIdSocketHandle.Bind;
  315. var
  316. LValue: Integer;
  317. begin
  318. LValue := iif(
  319. (FReuseSocket = rsTrue) or ((FReuseSocket = rsOSDependent) and (GOSType = otUnix)),
  320. Id_SO_True,
  321. Id_SO_False
  322. );
  323. SetSockOpt(Id_SOL_SOCKET, Id_SO_REUSEADDR, LValue);
  324. {$IFDEF DCC}
  325. {$IFDEF LINUX64}
  326. // RLebeau 1/18/2016: Embarcadero's PAServer on Linux64 fails quickly with
  327. // "socket in use" errors without this option enabled. PAServer bug? For
  328. // now, noone else has complained about problems related to this option,
  329. // so let's limit this fix to just Delphi for now. Should we add a
  330. // HAS_SO_REUSEPORT define so FPC can use this too? What about adding a
  331. // new ReusePort property to configure this separately from ReuseSocket?
  332. // RLebeau 3/7/2017: Windows 10 has a Developer Mode that includes a Linux
  333. // Bash shell for running Linux executables directly in Windows. However,
  334. // PAServer fails to open a listening socket in this Shell with an
  335. // "Error #22 invalid argument" error. Since SO_REUSEPORT does not exist
  336. // on Windows, could that be why? Let's just ignore any socket errors here
  337. // for now...
  338. try
  339. SetSockOpt(Id_SOL_SOCKET, Id_SO_REUSEPORT, LValue);
  340. except
  341. on E: EIdSocketError do begin
  342. //if E.LastError <> EINVAL then raise;
  343. end;
  344. end;
  345. {$ENDIF}
  346. {$ENDIF}
  347. if (Port = 0) and (FClientPortMin <> 0) and (FClientPortMax <> 0) then begin
  348. if (FClientPortMin > FClientPortMax) then begin
  349. raise EIdInvalidPortRange.CreateFmt(RSInvalidPortRange, [FClientPortMin, FClientPortMax]);
  350. end else if not BindPortReserved then begin
  351. // TODO: skip BindPortReserved() and call GStack.Bind() directly so the
  352. // Exception.InnerException property can be set to report the real reason
  353. // why the port cannot be bound...
  354. raise EIdCanNotBindPortInRange.CreateFmt(RSCannotBindRange, [FClientPortMin, FClientPortMax]);
  355. end;
  356. end else {if not TryBind(Port) then} begin
  357. // RLebeau 1/8/2019: skipping TryBind() and calling GStack.Bind() directly so
  358. // the Exception.InnerException property can be set to report the real reason
  359. // why the port cannot be bound...
  360. //raise EIdCouldNotBindSocket.Create(RSCouldNotBindSocket);
  361. try
  362. GStack.Bind(Handle, FIP, Port, FIPVersion);
  363. UpdateBindingLocal;
  364. except
  365. IndyRaiseOuterException(EIdCouldNotBindSocket.Create(RSCouldNotBindSocket));
  366. end;
  367. end;
  368. end;
  369. procedure TIdSocketHandle.Broadcast(const AData: string; const APort: TIdPort;
  370. const AIP: String = ''; AByteEncoding: IIdTextEncoding = nil
  371. {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}
  372. );
  373. begin
  374. Broadcast(ToBytes(AData, AByteEncoding{$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF}), APort, AIP);
  375. end;
  376. procedure TIdSocketHandle.Broadcast(const AData: TIdBytes; const APort: TIdPort;
  377. const AIP: String = '');
  378. var
  379. LIP: String;
  380. begin
  381. LIP := Trim(AIP);
  382. if LIP = '' then begin
  383. if IPVersion = Id_IPv4 then begin
  384. // TODO: on Windows, use WSAIoctl(SIO_GET_BROADCAST_ADDRESS) instead.
  385. // On other platforms, use getifaddrs() or other suitable API to retreive
  386. // the broadcast IP if possible, or else the local IP/Subnet and then
  387. // calculate the broadcast IP manually...
  388. LIP := '255.255.255.255'; {Do not Localize}
  389. end else begin
  390. // IPv6 does not support broadcasts, multicast must be used instead...
  391. // TODO: make TIdStack.IPVersionUnsupported() public
  392. //GStack.IPVersionUnsupported;
  393. raise EIdIPVersionUnsupported.Create(RSIPVersionUnsupported);
  394. end;
  395. end else begin
  396. LIP := GStack.ResolveHost(LIP, IPVersion);
  397. end;
  398. SetBroadcastFlag(True);
  399. SendTo(LIP, APort, AData, IPVersion);
  400. BroadcastEnabledChanged;
  401. end;
  402. procedure TIdSocketHandle.BroadcastEnabledChanged;
  403. begin
  404. SetBroadcastFlag(FBroadcastEnabled);
  405. end;
  406. procedure TIdSocketHandle.SetPeer(const AIP: string; const APort: TIdPort; const AIPVersion : TIdIPVersion = ID_DEFAULT_IP_VERSION);
  407. begin
  408. FPeerIP := AIP;
  409. FPeerPort := APort;
  410. FIPVersion := AIPVersion;
  411. end;
  412. procedure TIdSocketHandle.SetBinding(const AIP: string; const APort: TIdPort; const AIPVersion : TIdIPVersion = ID_DEFAULT_IP_VERSION);
  413. begin
  414. FIP := AIP;
  415. FPort := APort;
  416. FIPVersion := AIPVersion;
  417. end;
  418. procedure TIdSocketHandle.SetBroadcastEnabled(const AValue: Boolean);
  419. begin
  420. if FBroadCastEnabled <> AValue then begin
  421. FBroadcastEnabled := AValue;
  422. if HandleAllocated then begin
  423. BroadcastEnabledChanged;
  424. end;
  425. end;
  426. end;
  427. procedure TIdSocketHandle.SetBroadcastFlag(const AEnabled: Boolean);
  428. begin
  429. SetSockOpt(Id_SOL_SOCKET, Id_SO_BROADCAST, iif(AEnabled, 1, 0));
  430. end;
  431. procedure TIdSocketHandle.SetOverLapped(const AValue:boolean);
  432. begin
  433. // TODO: check for HandleAllocated
  434. FOverLapped := AValue;
  435. end;
  436. procedure TIdSocketHandle.Listen(const AQueueCount: Integer = 5);
  437. begin
  438. GStack.Listen(Handle, AQueueCount);
  439. end;
  440. function TIdSocketHandle.Accept(ASocket: TIdStackSocketHandle): Boolean;
  441. var
  442. LAcceptedSocket: TIdStackSocketHandle;
  443. LIP: String;
  444. LPort: TIdPort;
  445. begin
  446. Reset;
  447. LAcceptedSocket := GStack.Accept(ASocket, LIP, LPort);
  448. Result := (LAcceptedSocket <> Id_INVALID_SOCKET);
  449. if Result then begin
  450. // TODO: do we need to lock FConnectionHandle here, like Connect() does?
  451. SetHandle(LAcceptedSocket);
  452. // UpdateBindingLocal is necessary as it may be listening on multiple IPs/Ports
  453. UpdateBindingLocal;
  454. //TODO: Could Peer binding ever be other than what we receive above?
  455. // Need to reread it? If not, use the Accept() overload that returns IPVersion
  456. // as well, and then call SetPeer() here...
  457. // SetPeer(LIP, LPort, LIPVersion);
  458. UpdateBindingPeer;
  459. end;
  460. end;
  461. constructor TIdSocketHandle.Create(ACollection: TCollection);
  462. begin
  463. inherited Create(ACollection);
  464. FUseNagle := True;
  465. FReuseSocket := rsOSDependent;
  466. FConnectionHandle := TIdCriticalSection.Create;
  467. FReadSocketList := TIdSocketList.CreateSocketList;
  468. Reset;
  469. FClientPortMin := 0;
  470. FClientPortMax := 0;
  471. FIPVersion := ID_DEFAULT_IP_VERSION;
  472. if Assigned(ACollection) then begin
  473. Port := TIdSocketHandles(ACollection).DefaultPort;
  474. end;
  475. end;
  476. function TIdSocketHandle.Readable(AMSec: Integer = IdTimeoutDefault): Boolean;
  477. function CheckIsReadable(ALMSec: Integer): Boolean;
  478. begin
  479. if not HandleAllocated then begin
  480. raise EIdConnClosedGracefully.Create(RSConnectionClosedGracefully);
  481. end;
  482. Result := Select(ALMSec);
  483. end;
  484. begin
  485. if AMSec = IdTimeoutDefault then begin
  486. AMSec := IdTimeoutInfinite;
  487. end;
  488. if TIdAntiFreezeBase.ShouldUse then begin
  489. if AMSec = IdTimeoutInfinite then begin
  490. repeat
  491. Result := CheckIsReadable(GAntiFreeze.IdleTimeOut);
  492. until Result;
  493. Exit;
  494. end;
  495. while AMSec > GAntiFreeze.IdleTimeOut do begin
  496. Result := CheckIsReadable(GAntiFreeze.IdleTimeOut);
  497. if Result then begin
  498. Exit;
  499. end;
  500. Dec(AMSec, GAntiFreeze.IdleTimeOut);
  501. end;
  502. end;
  503. Result := CheckIsReadable(AMSec);
  504. end;
  505. procedure TIdSocketHandle.Assign(Source: TPersistent);
  506. var
  507. LSource: TIdSocketHandle;
  508. begin
  509. if Source is TIdSocketHandle then begin
  510. LSource := TIdSocketHandle(Source);
  511. FIP := LSource.FIP;
  512. Port := LSource.Port;
  513. FPeerIP := LSource.FPeerIP;
  514. FPeerPort := LSource.FPeerPort;
  515. FIPVersion := LSource.IPVersion;
  516. end else begin
  517. inherited
  518. end;
  519. end;
  520. procedure TIdSocketHandle.UpdateBindingLocal;
  521. begin
  522. GStack.GetSocketName(Handle, FIP, FPort, FIPVersion);
  523. end;
  524. procedure TIdSocketHandle.UpdateBindingPeer;
  525. begin
  526. GStack.GetPeerName(Handle, FPeerIP, FPeerPort, FIPVersion);
  527. end;
  528. procedure TIdSocketHandle.Reset(const AResetLocal: boolean = True);
  529. begin
  530. SetHandle(Id_INVALID_SOCKET);
  531. if AResetLocal then begin
  532. FIP := '';
  533. FPort := 0;
  534. end;
  535. FPeerIP := '';
  536. FPeerPort := 0;
  537. FIPVersion := ID_DEFAULT_IP_VERSION;
  538. end;
  539. function TIdSocketHandle.TryBind(APort: TIdPort): Boolean;
  540. begin
  541. try
  542. GStack.Bind(Handle, FIP, APort, FIPVersion);
  543. Result := True;
  544. UpdateBindingLocal;
  545. except
  546. Result := False;
  547. end;
  548. end;
  549. function TIdSocketHandle.BindPortReserved: Boolean;
  550. var
  551. i : TIdPort;
  552. begin
  553. Result := False;
  554. for i := FClientPortMax downto FClientPortMin do begin
  555. if TryBind(i) then begin
  556. Result := True;
  557. Exit;
  558. end;
  559. end;
  560. end;
  561. procedure TIdSocketHandle.GetSockOpt(ALevel: TIdSocketOptionLevel; AOptName: TIdSocketOption; out VOptVal: Integer);
  562. begin
  563. GStack.GetSocketOption(Handle, ALevel, AOptName, VOptVal);
  564. end;
  565. function TIdSocketHandle.Select(ATimeOut: Integer = IdTimeoutInfinite): Boolean;
  566. begin
  567. Result := FReadSocketList.SelectRead(ATimeOut);
  568. TIdAntiFreezeBase.DoProcess(Result = False);
  569. end;
  570. procedure TIdSocketHandle.SetHandle(AHandle: TIdStackSocketHandle);
  571. var
  572. LOpt: Integer;
  573. begin
  574. if FHandle <> Id_INVALID_SOCKET then begin
  575. FReadSocketList.Remove(FHandle);
  576. end;
  577. FHandle := AHandle;
  578. FHandleAllocated := FHandle <> Id_INVALID_SOCKET;
  579. if FHandleAllocated then begin
  580. FReadSocketList.Add(FHandle);
  581. GetSockOpt(Id_SOL_SOCKET, Id_SO_TYPE, FSocketType);
  582. //Get the NODELAY Socket option if we have a TCP Socket.
  583. if SocketType = Id_SOCK_STREAM then begin
  584. GetSockOpt(Id_SOCKETOPTIONLEVEL_TCP, Id_TCP_NODELAY, LOpt);
  585. FUseNagle := (LOpt = 0);
  586. end;
  587. end else begin
  588. FSocketType := Id_SOCK_UNKNOWN;
  589. end;
  590. end;
  591. procedure TIdSocketHandle.SetIPVersion(const Value: TIdIPVersion);
  592. begin
  593. if Value <> FIPVersion then begin
  594. if HandleAllocated then begin
  595. raise EIdCannotSetIPVersionWhenConnected.Create(RSCannotSetIPVersionWhenConnected);
  596. end;
  597. FIPVersion := Value;
  598. end;
  599. end;
  600. procedure TIdSocketHandle.AddMulticastMembership(const AGroupIP: String);
  601. begin
  602. GStack.AddMulticastMembership(Handle, AGroupIP, FIP, FIPVersion);
  603. end;
  604. procedure TIdSocketHandle.DropMulticastMembership(const AGroupIP: String);
  605. begin
  606. GStack.DropMulticastMembership(Handle, AGroupIP, FIP, FIPVersion);
  607. end;
  608. procedure TIdSocketHandle.SetKeepAliveValues(const AEnabled: Boolean;
  609. const ATimeMS, AInterval: Integer);
  610. begin
  611. GStack.SetKeepAliveValues(Handle, AEnabled, ATimeMS, AInterval);
  612. end;
  613. procedure TIdSocketHandle.SetLoopBack(const AValue: Boolean);
  614. begin
  615. GStack.SetLoopBack(Handle, AValue, FIPVersion);
  616. end;
  617. procedure TIdSocketHandle.SetMulticastTTL(const AValue: Byte);
  618. begin
  619. GStack.SetMulticastTTL(Handle, AValue, FIPVersion);
  620. end;
  621. procedure TIdSocketHandle.SetNagleOpt(const AEnabled: Boolean);
  622. begin
  623. { You only want to set a Nagle option for TCP.}
  624. if HandleAllocated and (SocketType = Id_SOCK_STREAM) then begin
  625. SetSockOpt(Id_SOCKETOPTIONLEVEL_TCP, Id_TCP_NODELAY, Integer(not AEnabled));
  626. end;
  627. end;
  628. procedure TIdSocketHandle.SetTTL(const AValue: Integer);
  629. begin
  630. if FIPVersion = Id_IPv4 then begin
  631. SetSockOpt(Id_SOL_IP, Id_SO_IP_TTL, AValue);
  632. end else begin
  633. SetSockOpt(Id_SOL_IPv6, Id_IPV6_UNICAST_HOPS, AValue);
  634. end;
  635. end;
  636. procedure TIdSocketHandle.SetUseNagle(const AValue: Boolean);
  637. begin
  638. if FUseNagle <> AValue then begin
  639. FUseNagle := AValue;
  640. SetNagleOpt(FUseNagle);
  641. end;
  642. end;
  643. { TIdSocketHandles }
  644. function TIdSocketHandles.Add: TIdSocketHandle;
  645. begin
  646. Result := inherited Add as TIdSocketHandle;
  647. Result.Port := DefaultPort;
  648. end;
  649. function TIdSocketHandles.BindingByHandle(const AHandle: TIdStackSocketHandle): TIdSocketHandle;
  650. var
  651. i: integer;
  652. begin
  653. Result := nil;
  654. for i := Count-1 downto 0 do begin
  655. if Items[i].Handle = AHandle then begin
  656. Result := Items[i];
  657. Exit;
  658. end;
  659. end;
  660. end;
  661. constructor TIdSocketHandles.Create(AOwner: TComponent);
  662. begin
  663. inherited Create(AOwner, TIdSocketHandle);
  664. end;
  665. function TIdSocketHandles.GetItem(Index: Integer): TIdSocketHandle;
  666. begin
  667. Result := TIdSocketHandle(inherited Items[index]);
  668. end;
  669. procedure TIdSocketHandles.SetItem(Index: Integer; const Value: TIdSocketHandle);
  670. begin
  671. inherited SetItem(Index, Value);
  672. end;
  673. end.