IdSocketHandle.pas 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. { $HDR$}
  2. {**********************************************************************}
  3. { Unit archived using Team Coherence }
  4. { Team Coherence is Copyright 2002 by Quality Software Components }
  5. { }
  6. { For further information / comments, visit our WEB site at }
  7. { http://www.TeamCoherence.com }
  8. {**********************************************************************}
  9. {}
  10. { $Log: 10333: IdSocketHandle.pas
  11. {
  12. { Rev 1.2 4/26/04 12:35:48 PM RLebeau
  13. { Removed recursion from Readable()
  14. }
  15. {
  16. { Rev 1.1 4/21/04 11:20:44 AM RLebeau
  17. { Bug fix for TIdSocketHandlers.Add()
  18. }
  19. {
  20. { Rev 1.0 2002.11.12 10:52:20 PM czhower
  21. }
  22. unit IdSocketHandle;
  23. interface
  24. uses
  25. Classes,
  26. IdException,
  27. IdGlobal,
  28. IdStack, IdStackConsts;
  29. type
  30. TIdSocketHandle = class;
  31. TIdSocketHandles = class(TOwnedCollection)
  32. protected
  33. FDefaultPort: integer;
  34. //
  35. function GetItem(Index: Integer): TIdSocketHandle;
  36. procedure SetItem(Index: Integer; const Value: TIdSocketHandle);
  37. public
  38. constructor Create(AOwner: TComponent); reintroduce;
  39. function Add: TIdSocketHandle; reintroduce;
  40. function BindingByHandle(const AHandle: TIdStackSocketHandle): TIdSocketHandle;
  41. property Items[Index: Integer]: TIdSocketHandle read GetItem write SetItem; default;
  42. //
  43. property DefaultPort: integer read FDefaultPort write FDefaultPort;
  44. end;
  45. TIdSocketHandle = class(TCollectionItem)
  46. protected
  47. FHandle: TIdStackSocketHandle;
  48. FHandleAllocated: Boolean;
  49. FIP, FPeerIP: string;
  50. FPort, FPeerPort: integer;
  51. FClientPortMin,
  52. FClientPortMax : Integer;
  53. function TryBind : Boolean;
  54. function BindPortReserved : Boolean;
  55. public
  56. function Accept(ASocket: TIdStackSocketHandle): boolean;
  57. procedure AllocateSocket(const ASocketType: Integer = Id_SOCK_STREAM;
  58. const AProtocol: Integer = Id_IPPROTO_IP);
  59. // Returns True if error was ignored (Matches iIgnore), false if no error occurred
  60. procedure Assign(Source: TPersistent); override;
  61. procedure Bind;
  62. procedure CloseSocket(const AResetLocal: boolean = True); virtual;
  63. function Connect(const AFamily: Integer = Id_PF_INET): Integer; virtual;
  64. constructor Create(ACollection: TCollection); override;
  65. destructor Destroy; override;
  66. procedure GetSockOpt(level, optname: Integer; optval: PChar; optlen: Integer);
  67. procedure Listen(const anQueueCount: integer = 5);
  68. function Readable(AMSec: Integer = IdTimeoutDefault): boolean;
  69. function Recv(var ABuf; ALen, AFlags: Integer): Integer;
  70. function RecvFrom(var ABuffer; const ALength, AFlags: Integer; var VIP: string;
  71. var VPort: Integer): Integer; virtual;
  72. procedure Reset(const AResetLocal: boolean = True);
  73. function Send(var Buf; len, flags: Integer): Integer;
  74. procedure SendTo(const AIP: string; const APort: Integer; var ABuffer;
  75. const ABufferSize: Integer);
  76. procedure SetPeer(const asIP: string; anPort: integer);
  77. procedure SetSockOpt(level, optname: Integer; optval: PChar; optlen: Integer);
  78. function Select(ASocket: TIdStackSocketHandle; ATimeOut: Integer): boolean;
  79. procedure UpdateBindingLocal;
  80. procedure UpdateBindingPeer;
  81. //
  82. property HandleAllocated: Boolean read FHandleAllocated;
  83. property Handle: TIdStackSocketHandle read FHandle;
  84. property PeerIP: string read FPeerIP;
  85. property PeerPort: integer read FPeerPort;
  86. published
  87. property ClientPortMin : Integer read FClientPortMin write FClientPortMin default 0;
  88. property ClientPortMax : Integer read FClientPortMax write FClientPortMax default 0;
  89. property IP: string read FIP write FIP;
  90. property Port: integer read FPort write FPort;
  91. end;
  92. EIdSocketHandleError = class(EIdException);
  93. EIdPackageSizeTooBig = class(EIdSocketHandleError);
  94. EIdNotAllBytesSent = class (EIdSocketHandleError);
  95. EIdCouldNotBindSocket = class (EIdSocketHandleError);
  96. EIdCanNotBindPortInRange = class (EIdSocketHandleError);
  97. EIdInvalidPortRange = class(EIdSocketHandleError);
  98. implementation
  99. uses
  100. IdAntiFreezeBase,
  101. IdComponent,
  102. IdResourceStrings;
  103. { TIdSocketHandle }
  104. procedure TIdSocketHandle.AllocateSocket(const ASocketType: Integer = Id_SOCK_STREAM;
  105. const AProtocol: Integer = Id_IPPROTO_IP);
  106. begin
  107. // If we are reallocating a socket - close and destroy the old socket handle
  108. CloseSocket;
  109. if HandleAllocated then begin
  110. Reset;
  111. end;
  112. FHandle := GStack.CreateSocketHandle(ASocketType, AProtocol);
  113. FHandleAllocated := True;
  114. end;
  115. procedure TIdSocketHandle.CloseSocket(const AResetLocal: boolean = True);
  116. begin
  117. if HandleAllocated then begin
  118. // Must be first, closing socket will trigger some errors, and they
  119. // may then check (in other threads) Connected, which checks this.
  120. FHandleAllocated := False;
  121. GStack.WSShutdown(Handle, Id_SD_Default);
  122. // SO_LINGER is false - socket may take a little while to actually close after this
  123. GStack.WSCloseSocket(Handle);
  124. end;
  125. end;
  126. function TIdSocketHandle.Connect(const AFamily: Integer = Id_PF_INET): Integer;
  127. begin
  128. Result := GStack.WSConnect(Handle, AFamily, PeerIP, PeerPort);
  129. if Result <> Id_Socket_Error then begin
  130. // UpdateBindingLocal needs to be called even though Bind calls it. After Bind is may be
  131. // 0.0.0.0 (INADDR_ANY). After connect it will be a real IP.
  132. UpdateBindingLocal;
  133. //TODO: Could Peer binding ever be other than what we specified above? Need to reread it?
  134. UpdateBindingPeer;
  135. end;
  136. end;
  137. destructor TIdSocketHandle.Destroy;
  138. begin
  139. CloseSocket;
  140. inherited;
  141. end;
  142. function TIdSocketHandle.Recv(var ABuf; ALen, AFlags: Integer): Integer;
  143. begin
  144. result := GStack.WSRecv(Handle, ABuf, ALen, AFlags);
  145. end;
  146. function TIdSocketHandle.Send(var Buf; len, flags: Integer): Integer;
  147. begin
  148. result := GStack.WSSend(Handle, Buf, len, flags);
  149. end;
  150. procedure TIdSocketHandle.SetSockOpt(level, optname: Integer; optval: PChar; optlen: Integer);
  151. begin
  152. GStack.CheckForSocketError(GStack.WSSetSockOpt(Handle, level, optname, optval, optlen));
  153. end;
  154. procedure TIdSocketHandle.SendTo(const AIP: string; const APort: Integer; var ABuffer;
  155. const ABufferSize: Integer);
  156. var
  157. BytesOut: Integer;
  158. begin
  159. BytesOut := GStack.WSSendTo(Handle, ABuffer, ABufferSize, 0, AIP, APort);
  160. if BytesOut = Id_SOCKET_ERROR then begin
  161. if GStack.WSGetLastError() = Id_WSAEMSGSIZE then begin
  162. raise EIdPackageSizeTooBig.Create(RSPackageSizeTooBig);
  163. end else begin
  164. GStack.CheckForSocketError;
  165. end;
  166. end else if BytesOut <> ABufferSize then begin
  167. raise EIdNotAllBytesSent.Create(RSNotAllBytesSent);
  168. end;
  169. end;
  170. function TIdSocketHandle.RecvFrom(var ABuffer; const ALength, AFlags: Integer; var VIP: string;
  171. var VPort: Integer): Integer;
  172. begin
  173. result := GStack.WSRecvFrom(Handle, ABuffer, ALength, AFlags, VIP, VPort);
  174. end;
  175. procedure TIdSocketHandle.Bind;
  176. begin
  177. if (Port = 0) and (FClientPortMin <> 0) and (FClientPortMax <> 0) then
  178. begin
  179. if (FClientPortMin > FClientPortMax) then begin
  180. Raise EIdInvalidPortRange.CreateFmt( RSInvalidPortRange, [FClientPortMin, FClientPortMax]);
  181. end
  182. else
  183. begin
  184. if not BindPortReserved then
  185. Raise EIdCanNotBindPortInRange.CreateFmt( RSCanNotBindRange ,[FClientPortMin,
  186. FClientPortMax]);
  187. end;
  188. end
  189. else
  190. begin
  191. if not TryBind then begin
  192. raise EIdCouldNotBindSocket.Create(RSCouldNotBindSocket);
  193. end;
  194. end;
  195. end;
  196. procedure TIdSocketHandle.SetPeer(const asIP: string; anPort: integer);
  197. begin
  198. FPeerIP := asIP;
  199. FPeerPort := anPort;
  200. end;
  201. procedure TIdSocketHandle.Listen(const anQueueCount: integer);
  202. begin
  203. GStack.CheckForSocketError(GStack.WSListen(Handle, anQueueCount));
  204. end;
  205. function TIdSocketHandle.Accept(ASocket: TIdStackSocketHandle): boolean;
  206. var
  207. LAcceptedSocket: TIdStackSocketHandle;
  208. begin
  209. Reset;
  210. LAcceptedSocket := GStack.WSAccept(ASocket, FIP, FPort);
  211. Result := not GStack.CheckForSocketError(LAcceptedSocket,
  212. [Id_WSAEINTR, // CloseSocket while in Accept
  213. Id_WSAENOTSOCK]); // CloseSocket just prior to Accept
  214. if Result then begin
  215. FHandle := LAcceptedSocket;
  216. FHandleAllocated := True;
  217. // UpdateBindingLocal is necessary as it may be listening on multiple IPs/Ports
  218. UpdateBindingLocal;
  219. UpdateBindingPeer;
  220. end;
  221. end;
  222. constructor TIdSocketHandle.Create(ACollection: TCollection);
  223. begin
  224. inherited Create(ACollection);
  225. Reset;
  226. FClientPortMin := 0;
  227. FClientPortMax := 0;
  228. if assigned(ACollection) then begin
  229. Port := TIdSocketHandles(ACollection).DefaultPort;
  230. end;
  231. end;
  232. function TIdSocketHandle.Readable(AMSec: Integer = IdTimeoutDefault): boolean;
  233. function CheckIsReadable(AMSec: Integer): Boolean;
  234. var
  235. ReadList: TList;
  236. begin
  237. if not FHandleAllocated then begin
  238. raise EIdConnClosedGracefully.Create(RSConnectionClosedGracefully);
  239. end;
  240. ReadList := TList.Create; try
  241. ReadList.Add(Pointer(Handle));
  242. Result := GStack.WSSelect(ReadList, nil, nil, AMSec) = 1;
  243. TIdAntiFreezeBase.DoProcess(Result = False);
  244. finally ReadList.Free; end;
  245. end;
  246. begin
  247. if TIdAntiFreezeBase.ShouldUse then begin
  248. if AMSec = IdTimeoutInfinite then begin
  249. repeat
  250. Result := CheckIsReadable(GAntiFreeze.IdleTimeOut);
  251. until Result;
  252. Exit;
  253. end;
  254. while AMSec > GAntiFreeze.IdleTimeOut do begin
  255. Result := CheckIsReadable(GAntiFreeze.IdleTimeOut);
  256. if Result then begin
  257. Exit;
  258. end;
  259. Dec(AMSec, GAntiFreeze.IdleTimeOut);
  260. end;
  261. end;
  262. Result := CheckIsReadable(AMSec);
  263. end;
  264. procedure TIdSocketHandle.Assign(Source: TPersistent);
  265. var
  266. LSource: TIdSocketHandle;
  267. begin
  268. if ClassType <> Source.ClassType then begin
  269. inherited
  270. end else begin
  271. LSource := TIdSocketHandle(Source);
  272. IP := LSource.IP;
  273. Port := LSource.Port;
  274. FPeerIP := LSource.PeerIP;
  275. FPeerPort := LSource.PeerPort;
  276. end;
  277. end;
  278. procedure TIdSocketHandle.UpdateBindingLocal;
  279. var
  280. LFamily: integer;
  281. begin
  282. GStack.WSGetSockName(Handle, LFamily, FIP, FPort);
  283. end;
  284. procedure TIdSocketHandle.UpdateBindingPeer;
  285. var
  286. LFamily: integer;
  287. begin
  288. GStack.WSGetPeerName(Handle, LFamily, FPeerIP, FPeerPort);
  289. end;
  290. procedure TIdSocketHandle.Reset(const AResetLocal: boolean = True);
  291. begin
  292. FHandleAllocated := False;
  293. FHandle := Id_INVALID_SOCKET;
  294. if AResetLocal then begin
  295. FIP := ''; {Do not Localize}
  296. FPort := 0;
  297. end;
  298. FPeerIP := ''; {Do not Localize}
  299. FPeerPort := 0;
  300. end;
  301. function TIdSocketHandle.TryBind: Boolean;
  302. begin
  303. Result := not GStack.CheckForSocketError(GStack.WSBind(Handle, Id_PF_INET, IP, Port)
  304. , [Id_WSAEADDRINUSE]);
  305. if Result then begin
  306. UpdateBindingLocal;
  307. end;
  308. end;
  309. function TIdSocketHandle.BindPortReserved: Boolean;
  310. var
  311. i : Integer;
  312. begin
  313. Result := false;
  314. for i := FClientPortMax downto FClientPortMin do begin
  315. FPort := i;
  316. if TryBind then begin
  317. Result := True;
  318. Exit;
  319. end;
  320. end;
  321. end;
  322. procedure TIdSocketHandle.GetSockOpt(level, optname: Integer; optval: PChar; optlen: Integer);
  323. begin
  324. GStack.CheckForSocketError(GStack.WSGetSockOpt(Handle, level, optname, optval, optlen));
  325. end;
  326. function TIdSocketHandle.Select(ASocket: TIdStackSocketHandle;
  327. ATimeOut: Integer): boolean;
  328. var
  329. ReadList: TList;
  330. begin
  331. ReadList := TList.Create; try
  332. ReadList.Add(Pointer(ASocket));
  333. Result := GStack.WSSelect(ReadList, nil, nil, ATimeOut) = 1;
  334. TIdAntiFreezeBase.DoProcess(result = false);
  335. finally ReadList.free; end;
  336. end;
  337. { TIdSocketHandles }
  338. function TIdSocketHandles.Add: TIdSocketHandle;
  339. begin
  340. Result := TIdSocketHandle(Inherited Add);
  341. Result.Port := DefaultPort;
  342. end;
  343. function TIdSocketHandles.BindingByHandle(const AHandle: TIdStackSocketHandle): TIdSocketHandle;
  344. var
  345. i: integer;
  346. begin
  347. Result := nil;
  348. i := Count - 1;
  349. while (i >= 0) and (Items[i].Handle <> AHandle) do begin
  350. dec(i);
  351. end;
  352. if i >= 0 then begin
  353. Result := Items[i];
  354. end;
  355. end;
  356. constructor TIdSocketHandles.Create(AOwner: TComponent);
  357. begin
  358. inherited Create(AOwner, TIdSocketHandle);
  359. end;
  360. function TIdSocketHandles.GetItem(Index: Integer): TIdSocketHandle;
  361. begin
  362. Result := TIdSocketHandle(inherited Items[index]);
  363. end;
  364. procedure TIdSocketHandles.SetItem(Index: Integer; const Value: TIdSocketHandle);
  365. begin
  366. inherited SetItem(Index, Value);
  367. end;
  368. end.