IdUDPBase.pas 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  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.15 11/12/2004 11:30:18 AM JPMugaas
  18. Expansions for IPv6.
  19. Rev 1.14 11/11/04 12:05:32 PM RLebeau
  20. Updated ReceiveBuffer() to set AMSec to IdTimeoutInfinite when the
  21. ReceiveTimeout property is 0
  22. Rev 1.13 11/7/2004 11:33:30 PM JPMugaas
  23. Now uses Connect, Disconnect, Send, and Receive similarly to the TCP Clients.
  24. This should prevent unneeded DNS name to IP address conversions that SendTo
  25. was doing.
  26. Rev 1.12 7/21/04 3:33:10 PM RLebeau
  27. Updated TIdUDPBase.ReceiveString() to use new BytesToString() parameters
  28. Rev 1.11 09/06/2004 00:29:56 CCostelloe
  29. Kylix 3 patch
  30. Rev 1.10 2004.02.03 4:17:00 PM czhower
  31. For unit name changes.
  32. Rev 1.9 21.1.2004 ã. 12:31:00 DBondzhev
  33. Fix for Indy source. Workaround for dccil bug
  34. now it can be compiled using Compile instead of build
  35. Rev 1.7 10/26/2003 12:30:18 PM BGooijen
  36. DotNet
  37. Rev 1.6 10/24/2003 5:18:36 PM BGooijen
  38. Removed boolean shortcutting from .GetActive
  39. Rev 1.5 10/22/2003 04:40:58 PM JPMugaas
  40. Should compile with some restored functionality. Still not finished.
  41. Rev 1.4 10/19/2003 9:34:30 PM BGooijen
  42. SetSocketOption
  43. Rev 1.3 2003.10.11 9:58:48 PM czhower
  44. Started on some todos
  45. Rev 1.2 2003.10.11 5:52:10 PM czhower
  46. -VCL fixes for servers
  47. -Chain suport for servers (Super core)
  48. -Scheduler upgrades
  49. -Full yarn support
  50. Rev 1.1 2003.09.30 1:23:08 PM czhower
  51. Stack split for DotNet
  52. Rev 1.0 11/13/2002 09:02:06 AM JPMugaas
  53. }
  54. unit IdUDPBase;
  55. interface
  56. {$I IdCompilerDefines.inc}
  57. //here to flip FPC into Delphi mode
  58. uses
  59. Classes,
  60. IdComponent,
  61. IdGlobal,
  62. IdException,
  63. IdSocketHandle;
  64. (*$HPPEMIT '#if defined(_VCL_ALIAS_RECORDS)' *)
  65. (*$HPPEMIT '#if !defined(UNICODE)' *)
  66. (*$HPPEMIT '#pragma alias "@Idudpbase@TIdUDPBase@SetPortA$qqrxus"="@Idudpbase@TIdUDPBase@SetPort$qqrxus"' *)
  67. (*$HPPEMIT '#else' *)
  68. (*$HPPEMIT '#pragma alias "@Idudpbase@TIdUDPBase@SetPortW$qqrxus"="@Idudpbase@TIdUDPBase@SetPort$qqrxus"' *)
  69. (*$HPPEMIT '#endif' *)
  70. (*$HPPEMIT '#endif' *)
  71. const
  72. ID_UDP_BUFFERSIZE = 8192;
  73. type
  74. TIdUDPBase = class(TIdComponent)
  75. protected
  76. FBinding: TIdSocketHandle;
  77. FBufferSize: Integer;
  78. FDsgnActive: Boolean;
  79. FHost: String;
  80. FPort: TIdPort;
  81. FReceiveTimeout: Integer;
  82. FReuseSocket: TIdReuseSocket;
  83. FIPVersion: TIdIPVersion;
  84. //
  85. FBroadcastEnabled: Boolean;
  86. procedure BroadcastEnabledChanged; dynamic;
  87. procedure CloseBinding; virtual;
  88. function GetActive: Boolean; virtual;
  89. procedure SetActive(const Value: Boolean);
  90. procedure SetBroadcastEnabled(const AValue: Boolean);
  91. function GetBinding: TIdSocketHandle; virtual; abstract;
  92. procedure Loaded; override;
  93. function GetIPVersion: TIdIPVersion; virtual;
  94. procedure SetIPVersion(const AValue: TIdIPVersion); virtual;
  95. function GetHost : String; virtual;
  96. procedure SetHost(const AValue : String); virtual;
  97. function GetPort : TIdPort; virtual;
  98. procedure SetPort(const AValue : TIdPort); virtual;
  99. property Host: string read GetHost write SetHost;
  100. property Port: TIdPort read GetPort write SetPort;
  101. public
  102. constructor Create(AOwner: TComponent); override;
  103. destructor Destroy; override;
  104. //
  105. property Binding: TIdSocketHandle read GetBinding;
  106. procedure Broadcast(const AData: string; const APort: TIdPort; const AIP: String = ''; AByteEncoding: IIdTextEncoding = nil); overload;
  107. procedure Broadcast(const AData: TIdBytes; const APort: TIdPort; const AIP: String = ''); overload;
  108. function ReceiveBuffer(var ABuffer : TIdBytes; var VPeerIP: string; var VPeerPort: TIdPort;
  109. AMSec: Integer = IdTimeoutDefault): integer; overload; virtual;
  110. function ReceiveBuffer(var ABuffer : TIdBytes; var VPeerIP: string; var VPeerPort: TIdPort;
  111. var VIPVersion: TIdIPVersion; const AMSec: Integer = IdTimeoutDefault): integer; overload; virtual;
  112. function ReceiveBuffer(var ABuffer : TIdBytes;
  113. const AMSec: Integer = IdTimeoutDefault): Integer; overload; virtual;
  114. function ReceiveString(const AMSec: Integer = IdTimeoutDefault; AByteEncoding: IIdTextEncoding = nil): string; overload;
  115. function ReceiveString(var VPeerIP: string; var VPeerPort: TIdPort;
  116. const AMSec: Integer = IdTimeoutDefault; AByteEncoding: IIdTextEncoding = nil): string; overload;
  117. procedure Send(const AHost: string; const APort: TIdPort; const AData: string; AByteEncoding: IIdTextEncoding = nil);
  118. procedure SendBuffer(const AHost: string; const APort: TIdPort; const AIPVersion: TIdIPVersion; const ABuffer : TIdBytes); overload; virtual;
  119. procedure SendBuffer(const AHost: string; const APort: TIdPort; const ABuffer: TIdBytes); overload; virtual;
  120. //
  121. property ReceiveTimeout: Integer read FReceiveTimeout write FReceiveTimeout default IdTimeoutInfinite;
  122. property ReuseSocket: TIdReuseSocket read FReuseSocket write FReuseSocket default rsOSDependent;
  123. published
  124. property Active: Boolean read GetActive write SetActive Default False;
  125. property BufferSize: Integer read FBufferSize write FBufferSize default ID_UDP_BUFFERSIZE;
  126. property BroadcastEnabled: Boolean read FBroadcastEnabled
  127. write SetBroadcastEnabled default False;
  128. property IPVersion: TIdIPVersion read GetIPVersion write SetIPVersion default ID_DEFAULT_IP_VERSION;
  129. end;
  130. EIdUDPException = class(EIdException);
  131. EIdUDPReceiveErrorZeroBytes = class(EIdUDPException);
  132. implementation
  133. uses
  134. IdStackConsts, IdStack, SysUtils;
  135. { TIdUDPBase }
  136. constructor TIdUDPBase.Create(AOwner: TComponent);
  137. begin
  138. inherited Create(AOwner);
  139. BufferSize := ID_UDP_BUFFERSIZE;
  140. FReceiveTimeout := IdTimeoutInfinite;
  141. FReuseSocket := rsOSDependent;
  142. FIPVersion := ID_DEFAULT_IP_VERSION;
  143. end;
  144. destructor TIdUDPBase.Destroy;
  145. begin
  146. Active := False;
  147. //double check that binding gets freed.
  148. //sometimes possible that binding is allocated, but active=false
  149. CloseBinding;
  150. inherited Destroy;
  151. end;
  152. procedure TIdUDPBase.Broadcast(const AData: string; const APort: TIdPort;
  153. const AIP: String = ''; AByteEncoding: IIdTextEncoding = nil);
  154. begin
  155. Binding.Broadcast(AData, APort, AIP, AByteEncoding);
  156. end;
  157. procedure TIdUDPBase.Broadcast(const AData: TIdBytes; const APort: TIdPort;
  158. const AIP: String = '');
  159. begin
  160. Binding.Broadcast(AData, APort, AIP);
  161. end;
  162. procedure TIdUDPBase.BroadcastEnabledChanged;
  163. begin
  164. if Assigned(FBinding) then begin
  165. FBinding.BroadcastEnabled := BroadcastEnabled;
  166. end;
  167. end;
  168. procedure TIdUDPBase.CloseBinding;
  169. begin
  170. FreeAndNil(FBinding);
  171. end;
  172. function TIdUDPBase.GetActive: Boolean;
  173. begin
  174. if IsDesignTime then begin
  175. Result := FDsgnActive;
  176. end else begin
  177. Result := Assigned(FBinding);
  178. if Result then begin
  179. Result := FBinding.HandleAllocated;
  180. end;
  181. end;
  182. end;
  183. function TIdUDPBase.GetHost: String;
  184. begin
  185. Result := FHost;
  186. end;
  187. function TIdUDPBase.GetIPVersion: TIdIPVersion;
  188. begin
  189. Result := FIPVersion;
  190. end;
  191. function TIdUDPBase.GetPort: TIdPort;
  192. begin
  193. Result := FPort;
  194. end;
  195. procedure TIdUDPBase.Loaded;
  196. var
  197. b: Boolean;
  198. begin
  199. inherited Loaded;
  200. b := FDsgnActive;
  201. FDsgnActive := False;
  202. Active := b;
  203. end;
  204. function TIdUDPBase.ReceiveBuffer(var ABuffer : TIdBytes;
  205. const AMSec: Integer = IdTimeoutDefault): Integer;
  206. var
  207. VoidIP: string;
  208. VoidPort: TIdPort;
  209. VoidIPVer: TIdIPVersion;
  210. begin
  211. Result := ReceiveBuffer(ABuffer, VoidIP, VoidPort, VoidIPVer, AMSec);
  212. end;
  213. function TIdUDPBase.ReceiveBuffer(var ABuffer : TIdBytes;
  214. var VPeerIP: string; var VPeerPort: TIdPort;
  215. AMSec: Integer = IdTimeoutDefault): integer;
  216. var
  217. VoidIPVer : TIdIPVersion;
  218. begin
  219. Result := ReceiveBuffer(ABuffer, VPeerIP, VPeerPort, VoidIPVer, AMSec);
  220. // GBSDStack.CheckForSocketError(Result);
  221. end;
  222. function TIdUDPBase.ReceiveBuffer(var ABuffer : TIdBytes;
  223. var VPeerIP: string; var VPeerPort: TIdPort; var VIPVersion: TIdIPVersion;
  224. const AMSec: Integer = IdTimeoutDefault): integer;
  225. var
  226. LMSec : Integer;
  227. begin
  228. if AMSec = IdTimeoutDefault then begin
  229. if ReceiveTimeOut = 0 then begin
  230. LMSec := IdTimeoutInfinite;
  231. end else begin
  232. LMSec := ReceiveTimeOut;
  233. end;
  234. end else begin
  235. LMSec := AMSec;
  236. end;
  237. if not Binding.Readable(LMSec) then begin
  238. Result := 0;
  239. VPeerIP := ''; {Do not Localize}
  240. VPeerPort := 0;
  241. Exit;
  242. end;
  243. Result := Binding.RecvFrom(ABuffer, VPeerIP, VPeerPort, VIPVersion);
  244. end;
  245. function TIdUDPBase.ReceiveString(var VPeerIP: string; var VPeerPort: TIdPort;
  246. const AMSec: Integer = IdTimeoutDefault; AByteEncoding: IIdTextEncoding = nil): string;
  247. var
  248. i: Integer;
  249. LBuffer : TIdBytes;
  250. begin
  251. SetLength(LBuffer, BufferSize);
  252. i := ReceiveBuffer(LBuffer, VPeerIP, VPeerPort, AMSec);
  253. Result := BytesToString(LBuffer, 0, i, AByteEncoding);
  254. end;
  255. function TIdUDPBase.ReceiveString(const AMSec: Integer = IdTimeoutDefault;
  256. AByteEncoding: IIdTextEncoding = nil): string;
  257. var
  258. VoidIP: string;
  259. VoidPort: TIdPort;
  260. begin
  261. Result := ReceiveString(VoidIP, VoidPort, AMSec, AByteEncoding);
  262. end;
  263. procedure TIdUDPBase.Send(const AHost: string; const APort: TIdPort; const AData: string;
  264. AByteEncoding: IIdTextEncoding = nil);
  265. begin
  266. SendBuffer(AHost, APort, ToBytes(AData, AByteEncoding));
  267. end;
  268. procedure TIdUDPBase.SendBuffer(const AHost: string; const APort: TIdPort; const ABuffer: TIdBytes);
  269. begin
  270. SendBuffer(AHost, APort, IPVersion, ABuffer);
  271. end;
  272. procedure TIdUDPBase.SendBuffer(const AHost: string; const APort: TIdPort;
  273. const AIPVersion: TIdIPVersion; const ABuffer: TIdBytes);
  274. var
  275. LIP : String;
  276. begin
  277. //TODO: fire OnStatus(hsResolving) event if AHost is not an IP address...
  278. {
  279. if AIPVersion = Id_IPv4 then
  280. begin
  281. if not GStack.IsIP(AHost) then begin
  282. if Assigned(OnStatus) then begin
  283. DoStatus(hsResolving, [AHost]);
  284. end;
  285. LIP := GStack.ResolveHost(AHost, AIPVersion);
  286. end else begin
  287. LIP := AHost;
  288. end;
  289. end
  290. else
  291. begin //IPv6
  292. LIP := MakeCanonicalIPv6Address(AHost);
  293. if LIP = '' then begin //if MakeCanonicalIPv6Address failed, we have a hostname
  294. if Assigned(OnStatus) then begin
  295. DoStatus(hsResolving, [AHost]);
  296. end;
  297. LIP := GStack.ResolveHost(AHost, AIPVersion);
  298. end else begin
  299. LIP := AHost;
  300. end;
  301. end;
  302. }
  303. LIP := GStack.ResolveHost(AHost, AIPVersion);
  304. Binding.SendTo(LIP, APort, ABuffer, AIPVersion);
  305. end;
  306. procedure TIdUDPBase.SetActive(const Value: Boolean);
  307. begin
  308. if Active <> Value then begin
  309. if not (IsDesignTime or IsLoading) then begin
  310. if Value then begin
  311. GetBinding;
  312. end
  313. else begin
  314. CloseBinding;
  315. end;
  316. end
  317. else begin // don't activate at designtime (or during loading of properties) {Do not Localize}
  318. FDsgnActive := Value;
  319. end;
  320. end;
  321. end;
  322. procedure TIdUDPBase.SetBroadcastEnabled(const AValue: Boolean);
  323. begin
  324. if FBroadCastEnabled <> AValue then begin
  325. FBroadcastEnabled := AValue;
  326. if Active then begin
  327. BroadcastEnabledChanged;
  328. end;
  329. end;
  330. end;
  331. procedure TIdUDPBase.SetHost(const AValue: String);
  332. begin
  333. FHost := Avalue;
  334. end;
  335. procedure TIdUDPBase.SetIPVersion(const AValue: TIdIPVersion);
  336. begin
  337. FIPVersion := AValue;
  338. end;
  339. procedure TIdUDPBase.SetPort(const AValue: TIdPort);
  340. begin
  341. FPort := AValue;
  342. end;
  343. end.