IdSocketHandle.pas 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761
  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 = ''; AByteEncoding: IIdTextEncoding = nil); overload;
  151. procedure Broadcast(const AData: TIdBytes; const APort: TIdPort; const AIP: String = ''); overload;
  152. procedure CloseSocket; virtual;
  153. procedure Connect; virtual;
  154. constructor Create(ACollection: TCollection); override;
  155. destructor Destroy; override;
  156. procedure Listen(const AQueueCount: Integer = 5);
  157. function Readable(AMSec: Integer = IdTimeoutDefault): boolean;
  158. function Receive(var VBuffer: TIdBytes): Integer;
  159. function RecvFrom(var ABuffer : TIdBytes; var VIP: string; var VPort: TIdPort; var VIPVersion: TIdIPVersion): Integer;
  160. procedure Reset(const AResetLocal: boolean = True);
  161. function Send(const AData: String; AByteEncoding: IIdTextEncoding = nil): Integer; overload;
  162. function Send(const ABuffer: TIdBytes; const AOffset: Integer = 0; const ASize: Integer = -1): Integer; overload;
  163. procedure SendTo(const AIP: string; const APort: TIdPort; const AData: String;
  164. const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION; AByteEncoding: IIdTextEncoding = nil); overload;
  165. procedure SendTo(const AIP: string; const APort: TIdPort; const ABuffer : TIdBytes; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION); overload;
  166. 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;
  167. procedure SetPeer(const AIP: string; const APort: TIdPort; const AIPVersion : TIdIPVersion = ID_DEFAULT_IP_VERSION);
  168. procedure SetBinding(const AIP: string; const APort: TIdPort; const AIPVersion : TIdIPVersion = ID_DEFAULT_IP_VERSION);
  169. procedure GetSockOpt(ALevel: TIdSocketOptionLevel; AOptName: TIdSocketOption; out VOptVal: Integer);
  170. procedure SetSockOpt(ALevel: TIdSocketOptionLevel; AOptName: TIdSocketOption; AOptVal: Integer);
  171. function Select(ATimeout: Integer = IdTimeoutInfinite): Boolean;
  172. procedure UpdateBindingLocal;
  173. procedure UpdateBindingPeer;
  174. procedure AddMulticastMembership(const AGroupIP: String);
  175. procedure DropMulticastMembership(const AGroupIP: String);
  176. procedure SetKeepAliveValues(const AEnabled: Boolean; const ATimeMS, AInterval: Integer);
  177. procedure SetLoopBack(const AValue: Boolean);
  178. procedure SetMulticastTTL(const AValue: Byte);
  179. procedure SetTTL(const AValue: Integer);
  180. procedure SetNagleOpt(const AEnabled: Boolean);
  181. //
  182. property HandleAllocated: Boolean read FHandleAllocated;
  183. property Handle: TIdStackSocketHandle read FHandle;
  184. property OverLapped: Boolean read FOverLapped write SetOverLapped;
  185. property PeerIP: string read FPeerIP;
  186. property PeerPort: TIdPort read FPeerPort;
  187. property SocketType : TIdSocketType read FSocketType;
  188. published
  189. property BroadcastEnabled: Boolean read FBroadcastEnabled write SetBroadcastEnabled default False;
  190. property ClientPortMin : TIdPort read FClientPortMin write FClientPortMin default DEF_PORT_ANY;
  191. property ClientPortMax : TIdPort read FClientPortMax write FClientPortMax default DEF_PORT_ANY;
  192. property IP: string read FIP write FIP;
  193. property IPVersion: TIdIPVersion read FIPVersion write SetIPVersion default ID_DEFAULT_IP_VERSION;
  194. property Port: TIdPort read FPort write FPort;
  195. property ReuseSocket: TIdReuseSocket read FReuseSocket write FReuseSocket default rsOSDependent;
  196. property UseNagle: Boolean read FUseNagle write SetUseNagle default True;
  197. end;
  198. TIdSocketHandleEvent = procedure(AHandle: TIdSocketHandle) of object;
  199. implementation
  200. uses
  201. {$IFDEF DCC_XE3_OR_ABOVE}
  202. System.SyncObjs,
  203. {$ENDIF}
  204. IdAntiFreezeBase, IdComponent, IdResourceStrings, SysUtils;
  205. { TIdSocketHandle }
  206. procedure TIdSocketHandle.AllocateSocket(const ASocketType: TIdSocketType;
  207. const AProtocol: TIdSocketProtocol);
  208. begin
  209. // If we are reallocating a socket - close and destroy the old socket handle
  210. CloseSocket;
  211. if HandleAllocated then begin
  212. Reset;
  213. end;
  214. // Set property so it calls the writer
  215. SetHandle(GStack.NewSocketHandle(ASocketType, AProtocol, FIPVersion, FOverLapped));
  216. end;
  217. procedure TIdSocketHandle.Disconnect;
  218. begin
  219. GStack.Disconnect(Handle);
  220. end;
  221. procedure TIdSocketHandle.CloseSocket;
  222. begin
  223. FConnectionHandle.Enter;
  224. try
  225. if HandleAllocated then begin
  226. // Must be first, closing socket will trigger some errors, and they
  227. // may then call (in other threads) Connected, which in turn looks at
  228. // FHandleAllocated.
  229. FHandleAllocated := False;
  230. Disconnect;
  231. SetHandle(Id_INVALID_SOCKET);
  232. end;
  233. finally
  234. FConnectionHandle.Leave;
  235. end;
  236. end;
  237. procedure TIdSocketHandle.Connect;
  238. begin
  239. GStack.Connect(Handle, PeerIP, PeerPort, FIPVersion);
  240. FConnectionHandle.Enter;
  241. try
  242. if HandleAllocated then begin
  243. // UpdateBindingLocal needs to be called even though Bind calls it. After
  244. // Bind is may be 0.0.0.0 (INADDR_ANY). After connect it will be a real IP.
  245. UpdateBindingLocal;
  246. //TODO: Could Peer binding ever be other than what we specified above?
  247. // Need to reread it? If not, call SetPeer() here...
  248. // SetPeer(PeerIP, PeerPort, FIPVersion);
  249. UpdateBindingPeer;
  250. end;
  251. finally
  252. FConnectionHandle.Leave;
  253. end;
  254. end;
  255. destructor TIdSocketHandle.Destroy;
  256. begin
  257. CloseSocket;
  258. FConnectionHandle.Free;
  259. FReadSocketList.Free;
  260. inherited Destroy;
  261. end;
  262. function TIdSocketHandle.Receive(var VBuffer: TIdBytes): Integer;
  263. begin
  264. Result := GStack.Receive(Handle, VBuffer);
  265. end;
  266. function TIdSocketHandle.Send(const AData: String; AByteEncoding: IIdTextEncoding = nil): Integer;
  267. begin
  268. Result := Send(ToBytes(AData, AByteEncoding));
  269. end;
  270. function TIdSocketHandle.Send(const ABuffer: TIdBytes; const AOffset: Integer = 0;
  271. const ASize: Integer = -1): Integer;
  272. begin
  273. Result := GStack.Send(Handle, ABuffer, AOffset, ASize);
  274. end;
  275. procedure TIdSocketHandle.SetSockOpt(ALevel: TIdSocketOptionLevel;
  276. AOptName: TIdSocketOption; AOptVal: Integer);
  277. begin
  278. GStack.SetSocketOption(Handle, ALevel, AOptName, AOptVal);
  279. end;
  280. procedure TIdSocketHandle.SendTo(const AIP: string; const APort: TIdPort;
  281. const AData: String; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION;
  282. AByteEncoding: IIdTextEncoding = nil);
  283. begin
  284. SendTo(AIP, APort, ToBytes(AData, AByteEncoding), AIPVersion);
  285. end;
  286. procedure TIdSocketHandle.SendTo(const AIP: string; const APort: TIdPort;
  287. const ABuffer : TIdBytes; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION);
  288. begin
  289. SendTo(AIP, APort, ABuffer, 0, -1, AIPVersion);
  290. end;
  291. procedure TIdSocketHandle.SendTo(const AIP: string; const APort: TIdPort;
  292. const ABuffer : TIdBytes; const AOffset: Integer; const ASize: Integer;
  293. const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION);
  294. begin
  295. GStack.SendTo(Handle, ABuffer, AOffset, ASize, AIP, APort, AIPVersion);
  296. end;
  297. function TIdSocketHandle.RecvFrom(var ABuffer : TIdBytes; var VIP: string;
  298. var VPort: TIdPort; var VIPVersion: TIdIPVersion): Integer;
  299. begin
  300. Result := GStack.ReceiveFrom(Handle, ABuffer, VIP, VPort, VIPVersion);
  301. end;
  302. procedure TIdSocketHandle.Bind;
  303. var
  304. LValue: Integer;
  305. begin
  306. LValue := iif(
  307. (FReuseSocket = rsTrue) or ((FReuseSocket = rsOSDependent) and (GOSType = otUnix)),
  308. Id_SO_True,
  309. Id_SO_False
  310. );
  311. SetSockOpt(Id_SOL_SOCKET, Id_SO_REUSEADDR, LValue);
  312. {$IF DEFINED(DCC) AND DEFINED(LINUX64)}
  313. // RLebeau 1/18/2016: Embarcadero's PAServer on Linux64 fails quickly with
  314. // "socket in use" errors without this option enabled. PAServer bug? For
  315. // now, noone else has complained about problems related to this option,
  316. // so let's limit this fix to just Delphi for now. Should we add a
  317. // HAS_SO_REUSEPORT define so FPC can use this too? What about adding a
  318. // new ReusePort property to configure this separately from ReuseSocket?
  319. // RLebeau 3/7/2017: Windows 10 has a Developer Mode that includes a Linux
  320. // Bash shell for running Linux executables directly in Windows. However,
  321. // PAServer fails to open a listening socket in this Shell with an
  322. // "Error #22 invalid argument" error. Since SO_REUSEPORT does not exist
  323. // on Windows, could that be why? Let's just ignore any socket errors here
  324. // for now...
  325. try
  326. SetSockOpt(Id_SOL_SOCKET, Id_SO_REUSEPORT, LValue);
  327. except
  328. on E: EIdSocketError do begin
  329. //if E.LastError <> EINVAL then raise;
  330. end;
  331. end;
  332. {$IFEND}
  333. if (Port = 0) and (FClientPortMin <> 0) and (FClientPortMax <> 0) then begin
  334. if (FClientPortMin > FClientPortMax) then begin
  335. raise EIdInvalidPortRange.CreateFmt(RSInvalidPortRange, [FClientPortMin, FClientPortMax]);
  336. end else if not BindPortReserved then begin
  337. // TODO: skip BindPortReserved() and call GStack.Bind() directly so the
  338. // Exception.InnerException property can be set to report the real reason
  339. // why the port cannot be bound...
  340. raise EIdCanNotBindPortInRange.CreateFmt(RSCannotBindRange, [FClientPortMin, FClientPortMax]);
  341. end;
  342. end else {if not TryBind(Port) then} begin
  343. // RLebeau 1/8/2019: skipping TryBind() and calling GStack.Bind() directly so
  344. // the Exception.InnerException property can be set to report the real reason
  345. // why the port cannot be bound...
  346. //raise EIdCouldNotBindSocket.Create(RSCouldNotBindSocket);
  347. try
  348. GStack.Bind(Handle, FIP, Port, FIPVersion);
  349. UpdateBindingLocal;
  350. except
  351. IndyRaiseOuterException(EIdCouldNotBindSocket.Create(RSCouldNotBindSocket));
  352. end;
  353. end;
  354. end;
  355. procedure TIdSocketHandle.Broadcast(const AData: string; const APort: TIdPort;
  356. const AIP: String = ''; AByteEncoding: IIdTextEncoding = nil);
  357. begin
  358. Broadcast(ToBytes(AData, AByteEncoding), APort, AIP);
  359. end;
  360. procedure TIdSocketHandle.Broadcast(const AData: TIdBytes; const APort: TIdPort;
  361. const AIP: String = '');
  362. var
  363. LIP: String;
  364. begin
  365. LIP := Trim(AIP);
  366. if LIP = '' then begin
  367. if IPVersion = Id_IPv4 then begin
  368. // TODO: on Windows, use WSAIoctl(SIO_GET_BROADCAST_ADDRESS) instead.
  369. // On other platforms, use getifaddrs() or other suitable API to retreive
  370. // the broadcast IP if possible, or else the local IP/Subnet and then
  371. // calculate the broadcast IP manually...
  372. LIP := '255.255.255.255'; {Do not Localize}
  373. end else begin
  374. // IPv6 does not support broadcasts, multicast must be used instead...
  375. // TODO: make TIdStack.IPVersionUnsupported() public
  376. //GStack.IPVersionUnsupported;
  377. raise EIdIPVersionUnsupported.Create(RSIPVersionUnsupported);
  378. end;
  379. end else begin
  380. LIP := GStack.ResolveHost(LIP, IPVersion);
  381. end;
  382. SetBroadcastFlag(True);
  383. SendTo(LIP, APort, AData, IPVersion);
  384. BroadcastEnabledChanged;
  385. end;
  386. procedure TIdSocketHandle.BroadcastEnabledChanged;
  387. begin
  388. SetBroadcastFlag(FBroadcastEnabled);
  389. end;
  390. procedure TIdSocketHandle.SetPeer(const AIP: string; const APort: TIdPort;
  391. const AIPVersion : TIdIPVersion = ID_DEFAULT_IP_VERSION);
  392. begin
  393. FPeerIP := AIP;
  394. FPeerPort := APort;
  395. FIPVersion := AIPVersion;
  396. end;
  397. procedure TIdSocketHandle.SetBinding(const AIP: string; const APort: TIdPort;
  398. const AIPVersion : TIdIPVersion = ID_DEFAULT_IP_VERSION);
  399. begin
  400. FIP := AIP;
  401. FPort := APort;
  402. FIPVersion := AIPVersion;
  403. end;
  404. procedure TIdSocketHandle.SetBroadcastEnabled(const AValue: Boolean);
  405. begin
  406. if FBroadCastEnabled <> AValue then begin
  407. FBroadcastEnabled := AValue;
  408. if HandleAllocated then begin
  409. BroadcastEnabledChanged;
  410. end;
  411. end;
  412. end;
  413. procedure TIdSocketHandle.SetBroadcastFlag(const AEnabled: Boolean);
  414. begin
  415. SetSockOpt(Id_SOL_SOCKET, Id_SO_BROADCAST, iif(AEnabled, 1, 0));
  416. end;
  417. procedure TIdSocketHandle.SetOverLapped(const AValue:boolean);
  418. begin
  419. // TODO: check for HandleAllocated
  420. FOverLapped := AValue;
  421. end;
  422. procedure TIdSocketHandle.Listen(const AQueueCount: Integer = 5);
  423. begin
  424. GStack.Listen(Handle, AQueueCount);
  425. end;
  426. function TIdSocketHandle.Accept(ASocket: TIdStackSocketHandle): Boolean;
  427. var
  428. LAcceptedSocket: TIdStackSocketHandle;
  429. LIP: String;
  430. LPort: TIdPort;
  431. begin
  432. Reset;
  433. LAcceptedSocket := GStack.Accept(ASocket, LIP, LPort);
  434. Result := (LAcceptedSocket <> Id_INVALID_SOCKET);
  435. if Result then begin
  436. // TODO: do we need to lock FConnectionHandle here, like Connect() does?
  437. SetHandle(LAcceptedSocket);
  438. // UpdateBindingLocal is necessary as it may be listening on multiple IPs/Ports
  439. UpdateBindingLocal;
  440. //TODO: Could Peer binding ever be other than what we receive above?
  441. // Need to reread it? If not, use the Accept() overload that returns IPVersion
  442. // as well, and then call SetPeer() here...
  443. // SetPeer(LIP, LPort, LIPVersion);
  444. UpdateBindingPeer;
  445. end;
  446. end;
  447. constructor TIdSocketHandle.Create(ACollection: TCollection);
  448. begin
  449. inherited Create(ACollection);
  450. FUseNagle := True;
  451. FReuseSocket := rsOSDependent;
  452. FConnectionHandle := TIdCriticalSection.Create;
  453. FReadSocketList := TIdSocketList.CreateSocketList;
  454. Reset;
  455. FClientPortMin := 0;
  456. FClientPortMax := 0;
  457. FIPVersion := ID_DEFAULT_IP_VERSION;
  458. if Assigned(ACollection) then begin
  459. Port := TIdSocketHandles(ACollection).DefaultPort;
  460. end;
  461. end;
  462. function TIdSocketHandle.Readable(AMSec: Integer = IdTimeoutDefault): Boolean;
  463. function CheckIsReadable(ALMSec: Integer): Boolean;
  464. begin
  465. if not HandleAllocated then begin
  466. raise EIdConnClosedGracefully.Create(RSConnectionClosedGracefully);
  467. end;
  468. Result := Select(ALMSec);
  469. end;
  470. begin
  471. if AMSec = IdTimeoutDefault then begin
  472. AMSec := IdTimeoutInfinite;
  473. end;
  474. if TIdAntiFreezeBase.ShouldUse then begin
  475. if AMSec = IdTimeoutInfinite then begin
  476. repeat
  477. Result := CheckIsReadable(GAntiFreeze.IdleTimeOut);
  478. until Result;
  479. Exit;
  480. end;
  481. while AMSec > GAntiFreeze.IdleTimeOut do begin
  482. Result := CheckIsReadable(GAntiFreeze.IdleTimeOut);
  483. if Result then begin
  484. Exit;
  485. end;
  486. Dec(AMSec, GAntiFreeze.IdleTimeOut);
  487. end;
  488. end;
  489. Result := CheckIsReadable(AMSec);
  490. end;
  491. procedure TIdSocketHandle.Assign(Source: TPersistent);
  492. var
  493. LSource: TIdSocketHandle;
  494. begin
  495. if Source is TIdSocketHandle then begin
  496. LSource := TIdSocketHandle(Source);
  497. FIP := LSource.FIP;
  498. Port := LSource.Port;
  499. FPeerIP := LSource.FPeerIP;
  500. FPeerPort := LSource.FPeerPort;
  501. FIPVersion := LSource.IPVersion;
  502. end else begin
  503. inherited
  504. end;
  505. end;
  506. procedure TIdSocketHandle.UpdateBindingLocal;
  507. begin
  508. GStack.GetSocketName(Handle, FIP, FPort, FIPVersion);
  509. end;
  510. procedure TIdSocketHandle.UpdateBindingPeer;
  511. begin
  512. GStack.GetPeerName(Handle, FPeerIP, FPeerPort, FIPVersion);
  513. end;
  514. procedure TIdSocketHandle.Reset(const AResetLocal: boolean = True);
  515. begin
  516. SetHandle(Id_INVALID_SOCKET);
  517. if AResetLocal then begin
  518. FIP := '';
  519. FPort := 0;
  520. end;
  521. FPeerIP := '';
  522. FPeerPort := 0;
  523. FIPVersion := ID_DEFAULT_IP_VERSION;
  524. end;
  525. function TIdSocketHandle.TryBind(APort: TIdPort): Boolean;
  526. begin
  527. try
  528. GStack.Bind(Handle, FIP, APort, FIPVersion);
  529. Result := True;
  530. UpdateBindingLocal;
  531. except
  532. Result := False;
  533. end;
  534. end;
  535. function TIdSocketHandle.BindPortReserved: Boolean;
  536. var
  537. i : TIdPort;
  538. begin
  539. Result := False;
  540. for i := FClientPortMax downto FClientPortMin do begin
  541. if TryBind(i) then begin
  542. Result := True;
  543. Exit;
  544. end;
  545. end;
  546. end;
  547. procedure TIdSocketHandle.GetSockOpt(ALevel: TIdSocketOptionLevel; AOptName: TIdSocketOption; out VOptVal: Integer);
  548. begin
  549. GStack.GetSocketOption(Handle, ALevel, AOptName, VOptVal);
  550. end;
  551. function TIdSocketHandle.Select(ATimeOut: Integer = IdTimeoutInfinite): Boolean;
  552. begin
  553. Result := FReadSocketList.SelectRead(ATimeOut);
  554. TIdAntiFreezeBase.DoProcess(Result = False);
  555. end;
  556. procedure TIdSocketHandle.SetHandle(AHandle: TIdStackSocketHandle);
  557. var
  558. LOpt: Integer;
  559. begin
  560. if FHandle <> Id_INVALID_SOCKET then begin
  561. FReadSocketList.Remove(FHandle);
  562. end;
  563. FHandle := AHandle;
  564. FHandleAllocated := FHandle <> Id_INVALID_SOCKET;
  565. if FHandleAllocated then begin
  566. FReadSocketList.Add(FHandle);
  567. GetSockOpt(Id_SOL_SOCKET, Id_SO_TYPE, FSocketType);
  568. //Get the NODELAY Socket option if we have a TCP Socket.
  569. if SocketType = Id_SOCK_STREAM then begin
  570. GetSockOpt(Id_SOCKETOPTIONLEVEL_TCP, Id_TCP_NODELAY, LOpt);
  571. FUseNagle := (LOpt = 0);
  572. end;
  573. end else begin
  574. FSocketType := Id_SOCK_UNKNOWN;
  575. end;
  576. end;
  577. procedure TIdSocketHandle.SetIPVersion(const Value: TIdIPVersion);
  578. begin
  579. if Value <> FIPVersion then begin
  580. if HandleAllocated then begin
  581. raise EIdCannotSetIPVersionWhenConnected.Create(RSCannotSetIPVersionWhenConnected);
  582. end;
  583. FIPVersion := Value;
  584. end;
  585. end;
  586. procedure TIdSocketHandle.AddMulticastMembership(const AGroupIP: String);
  587. begin
  588. GStack.AddMulticastMembership(Handle, AGroupIP, FIP, FIPVersion);
  589. end;
  590. procedure TIdSocketHandle.DropMulticastMembership(const AGroupIP: String);
  591. begin
  592. GStack.DropMulticastMembership(Handle, AGroupIP, FIP, FIPVersion);
  593. end;
  594. procedure TIdSocketHandle.SetKeepAliveValues(const AEnabled: Boolean;
  595. const ATimeMS, AInterval: Integer);
  596. begin
  597. GStack.SetKeepAliveValues(Handle, AEnabled, ATimeMS, AInterval);
  598. end;
  599. procedure TIdSocketHandle.SetLoopBack(const AValue: Boolean);
  600. begin
  601. GStack.SetLoopBack(Handle, AValue, FIPVersion);
  602. end;
  603. procedure TIdSocketHandle.SetMulticastTTL(const AValue: Byte);
  604. begin
  605. GStack.SetMulticastTTL(Handle, AValue, FIPVersion);
  606. end;
  607. procedure TIdSocketHandle.SetNagleOpt(const AEnabled: Boolean);
  608. begin
  609. { You only want to set a Nagle option for TCP.}
  610. if HandleAllocated and (SocketType = Id_SOCK_STREAM) then begin
  611. SetSockOpt(Id_SOCKETOPTIONLEVEL_TCP, Id_TCP_NODELAY, Integer(not AEnabled));
  612. end;
  613. end;
  614. procedure TIdSocketHandle.SetTTL(const AValue: Integer);
  615. begin
  616. if FIPVersion = Id_IPv4 then begin
  617. SetSockOpt(Id_SOL_IP, Id_SO_IP_TTL, AValue);
  618. end else begin
  619. SetSockOpt(Id_SOL_IPv6, Id_IPV6_UNICAST_HOPS, AValue);
  620. end;
  621. end;
  622. procedure TIdSocketHandle.SetUseNagle(const AValue: Boolean);
  623. begin
  624. if FUseNagle <> AValue then begin
  625. FUseNagle := AValue;
  626. SetNagleOpt(FUseNagle);
  627. end;
  628. end;
  629. { TIdSocketHandles }
  630. function TIdSocketHandles.Add: TIdSocketHandle;
  631. begin
  632. Result := inherited Add as TIdSocketHandle;
  633. Result.Port := DefaultPort;
  634. end;
  635. function TIdSocketHandles.BindingByHandle(const AHandle: TIdStackSocketHandle): TIdSocketHandle;
  636. var
  637. i: integer;
  638. begin
  639. Result := nil;
  640. for i := Count-1 downto 0 do begin
  641. if Items[i].Handle = AHandle then begin
  642. Result := Items[i];
  643. Exit;
  644. end;
  645. end;
  646. end;
  647. constructor TIdSocketHandles.Create(AOwner: TComponent);
  648. begin
  649. inherited Create(AOwner, TIdSocketHandle);
  650. end;
  651. function TIdSocketHandles.GetItem(Index: Integer): TIdSocketHandle;
  652. begin
  653. Result := TIdSocketHandle(inherited Items[index]);
  654. end;
  655. procedure TIdSocketHandles.SetItem(Index: Integer; const Value: TIdSocketHandle);
  656. begin
  657. inherited SetItem(Index, Value);
  658. end;
  659. end.