IdUDPBase.pas 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  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. IdComponent,
  60. IdGlobal,
  61. IdException,
  62. IdSocketHandle;
  63. (*$HPPEMIT '#if defined(_VCL_ALIAS_RECORDS)' *)
  64. (*$HPPEMIT '#if !defined(UNICODE)' *)
  65. (*$HPPEMIT '#pragma alias "@Idudpbase@TIdUDPBase@SetPortA$qqrxus"="@Idudpbase@TIdUDPBase@SetPort$qqrxus"' *)
  66. (*$HPPEMIT '#else' *)
  67. (*$HPPEMIT '#pragma alias "@Idudpbase@TIdUDPBase@SetPortW$qqrxus"="@Idudpbase@TIdUDPBase@SetPort$qqrxus"' *)
  68. (*$HPPEMIT '#endif' *)
  69. (*$HPPEMIT '#endif' *)
  70. const
  71. ID_UDP_BUFFERSIZE = 8192;
  72. type
  73. TIdUDPBase = class(TIdComponent)
  74. protected
  75. FBinding: TIdSocketHandle;
  76. FBufferSize: Integer;
  77. FDsgnActive: Boolean;
  78. FHost: String;
  79. FPort: TIdPort;
  80. FReceiveTimeout: Integer;
  81. FReuseSocket: TIdReuseSocket;
  82. FIPVersion: TIdIPVersion;
  83. //
  84. FBroadcastEnabled: Boolean;
  85. procedure BroadcastEnabledChanged; dynamic;
  86. procedure CloseBinding; virtual;
  87. function GetActive: Boolean; virtual;
  88. procedure InitComponent; override;
  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. destructor Destroy; override;
  103. //
  104. property Binding: TIdSocketHandle read GetBinding;
  105. procedure Broadcast(const AData: string; const APort: TIdPort; const AIP: String = '';
  106. AByteEncoding: IIdTextEncoding = nil
  107. {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}
  108. ); overload;
  109. procedure Broadcast(const AData: TIdBytes; const APort: TIdPort; const AIP: String = ''); overload;
  110. function ReceiveBuffer(var ABuffer : TIdBytes; var VPeerIP: string; var VPeerPort: TIdPort;
  111. AMSec: Integer = IdTimeoutDefault): integer; overload; virtual;
  112. function ReceiveBuffer(var ABuffer : TIdBytes; var VPeerIP: string; var VPeerPort: TIdPort;
  113. var VIPVersion: TIdIPVersion; const AMSec: Integer = IdTimeoutDefault): integer; overload; virtual;
  114. function ReceiveBuffer(var ABuffer : TIdBytes;
  115. const AMSec: Integer = IdTimeoutDefault): Integer; overload; virtual;
  116. function ReceiveString(const AMSec: Integer = IdTimeoutDefault; AByteEncoding: IIdTextEncoding = nil
  117. {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF}
  118. ): string; overload;
  119. function ReceiveString(var VPeerIP: string; var VPeerPort: TIdPort;
  120. const AMSec: Integer = IdTimeoutDefault; AByteEncoding: IIdTextEncoding = nil
  121. {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF}
  122. ): string; overload;
  123. procedure Send(const AHost: string; const APort: TIdPort; const AData: string;
  124. AByteEncoding: IIdTextEncoding = nil
  125. {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}
  126. );
  127. procedure SendBuffer(const AHost: string; const APort: TIdPort; const AIPVersion: TIdIPVersion; const ABuffer : TIdBytes); overload; virtual;
  128. procedure SendBuffer(const AHost: string; const APort: TIdPort; const ABuffer: TIdBytes); overload; virtual;
  129. //
  130. property ReceiveTimeout: Integer read FReceiveTimeout write FReceiveTimeout default IdTimeoutInfinite;
  131. property ReuseSocket: TIdReuseSocket read FReuseSocket write FReuseSocket default rsOSDependent;
  132. published
  133. property Active: Boolean read GetActive write SetActive Default False;
  134. property BufferSize: Integer read FBufferSize write FBufferSize default ID_UDP_BUFFERSIZE;
  135. property BroadcastEnabled: Boolean read FBroadcastEnabled
  136. write SetBroadcastEnabled default False;
  137. property IPVersion: TIdIPVersion read GetIPVersion write SetIPVersion default ID_DEFAULT_IP_VERSION;
  138. end;
  139. EIdUDPException = Class(EIdException);
  140. EIdUDPReceiveErrorZeroBytes = class(EIdUDPException);
  141. implementation
  142. uses
  143. IdStackConsts, IdStack, SysUtils;
  144. { TIdUDPBase }
  145. procedure TIdUDPBase.Broadcast(const AData: string; const APort: TIdPort;
  146. const AIP: String = ''; AByteEncoding: IIdTextEncoding = nil
  147. {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF});
  148. begin
  149. Binding.Broadcast(AData, APort, AIP, AByteEncoding{$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF});
  150. end;
  151. procedure TIdUDPBase.Broadcast(const AData: TIdBytes; const APort: TIdPort;
  152. const AIP: String = '');
  153. begin
  154. Binding.Broadcast(AData, APort, AIP);
  155. end;
  156. procedure TIdUDPBase.BroadcastEnabledChanged;
  157. begin
  158. if Assigned(FBinding) then begin
  159. FBinding.BroadcastEnabled := BroadcastEnabled;
  160. end;
  161. end;
  162. procedure TIdUDPBase.CloseBinding;
  163. begin
  164. FreeAndNil(FBinding);
  165. end;
  166. destructor TIdUDPBase.Destroy;
  167. begin
  168. Active := False;
  169. //double check that binding gets freed.
  170. //sometimes possible that binding is allocated, but active=false
  171. CloseBinding;
  172. inherited Destroy;
  173. end;
  174. function TIdUDPBase.GetActive: Boolean;
  175. begin
  176. if IsDesignTime then begin
  177. Result := FDsgnActive;
  178. end else begin
  179. Result := Assigned(FBinding);
  180. if Result then begin
  181. Result := FBinding.HandleAllocated;
  182. end;
  183. end;
  184. end;
  185. function TIdUDPBase.GetHost: String;
  186. begin
  187. Result := FHost;
  188. end;
  189. function TIdUDPBase.GetIPVersion: TIdIPVersion;
  190. begin
  191. Result := FIPVersion;
  192. end;
  193. function TIdUDPBase.GetPort: TIdPort;
  194. begin
  195. Result := FPort;
  196. end;
  197. procedure TIdUDPBase.InitComponent;
  198. begin
  199. inherited InitComponent;
  200. BufferSize := ID_UDP_BUFFERSIZE;
  201. FReceiveTimeout := IdTimeoutInfinite;
  202. FReuseSocket := rsOSDependent;
  203. FIPVersion := ID_DEFAULT_IP_VERSION;
  204. end;
  205. procedure TIdUDPBase.Loaded;
  206. var
  207. b: Boolean;
  208. begin
  209. inherited Loaded;
  210. b := FDsgnActive;
  211. FDsgnActive := False;
  212. Active := b;
  213. end;
  214. function TIdUDPBase.ReceiveBuffer(var ABuffer : TIdBytes;
  215. const AMSec: Integer = IdTimeoutDefault): Integer;
  216. var
  217. VoidIP: string;
  218. VoidPort: TIdPort;
  219. VoidIPVer: TIdIPVersion;
  220. begin
  221. Result := ReceiveBuffer(ABuffer, VoidIP, VoidPort, VoidIPVer, AMSec);
  222. end;
  223. function TIdUDPBase.ReceiveBuffer(var ABuffer : TIdBytes;
  224. var VPeerIP: string; var VPeerPort: TIdPort;
  225. AMSec: Integer = IdTimeoutDefault): integer;
  226. var
  227. VoidIPVer : TIdIPVersion;
  228. begin
  229. Result := ReceiveBuffer(ABuffer, VPeerIP, VPeerPort, VoidIPVer, AMSec);
  230. // GBSDStack.CheckForSocketError(Result);
  231. end;
  232. function TIdUDPBase.ReceiveBuffer(var ABuffer : TIdBytes;
  233. var VPeerIP: string; var VPeerPort: TIdPort; var VIPVersion: TIdIPVersion;
  234. const AMSec: Integer = IdTimeoutDefault): integer;
  235. var
  236. LMSec : Integer;
  237. begin
  238. if AMSec = IdTimeoutDefault then begin
  239. if ReceiveTimeOut = 0 then begin
  240. LMSec := IdTimeoutInfinite;
  241. end else begin
  242. LMSec := ReceiveTimeOut;
  243. end;
  244. end else begin
  245. LMSec := AMSec;
  246. end;
  247. if not Binding.Readable(LMSec) then begin
  248. Result := 0;
  249. VPeerIP := ''; {Do not Localize}
  250. VPeerPort := 0;
  251. Exit;
  252. end;
  253. Result := Binding.RecvFrom(ABuffer, VPeerIP, VPeerPort, VIPVersion);
  254. end;
  255. function TIdUDPBase.ReceiveString(var VPeerIP: string; var VPeerPort: TIdPort;
  256. const AMSec: Integer = IdTimeoutDefault; AByteEncoding: IIdTextEncoding = nil
  257. {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF}
  258. ): string;
  259. var
  260. i: Integer;
  261. LBuffer : TIdBytes;
  262. begin
  263. SetLength(LBuffer, BufferSize);
  264. i := ReceiveBuffer(LBuffer, VPeerIP, VPeerPort, AMSec);
  265. Result := BytesToString(LBuffer, 0, i, AByteEncoding{$IFDEF STRING_IS_ANSI}, ADestEncoding{$ENDIF});
  266. end;
  267. function TIdUDPBase.ReceiveString(const AMSec: Integer = IdTimeoutDefault;
  268. AByteEncoding: IIdTextEncoding = nil
  269. {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF}): string;
  270. var
  271. VoidIP: string;
  272. VoidPort: TIdPort;
  273. begin
  274. Result := ReceiveString(VoidIP, VoidPort, AMSec, AByteEncoding{$IFDEF STRING_IS_ANSI}, ADestEncoding{$ENDIF});
  275. end;
  276. procedure TIdUDPBase.Send(const AHost: string; const APort: TIdPort; const AData: string;
  277. AByteEncoding: IIdTextEncoding = nil
  278. {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}
  279. );
  280. begin
  281. SendBuffer(AHost, APort, ToBytes(AData, AByteEncoding{$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF}));
  282. end;
  283. procedure TIdUDPBase.SendBuffer(const AHost: string; const APort: TIdPort; const ABuffer: TIdBytes);
  284. begin
  285. SendBuffer(AHost, APort, IPVersion, ABuffer);
  286. end;
  287. procedure TIdUDPBase.SendBuffer(const AHost: string; const APort: TIdPort;
  288. const AIPVersion: TIdIPVersion; const ABuffer: TIdBytes);
  289. var
  290. LIP : String;
  291. begin
  292. //TODO: fire OnStatus(hsResolving) event if AHost is not an IP address...
  293. {
  294. if AIPVersion = Id_IPv4 then
  295. begin
  296. if not GStack.IsIP(AHost) then begin
  297. if Assigned(OnStatus) then begin
  298. DoStatus(hsResolving, [AHost]);
  299. end;
  300. LIP := GStack.ResolveHost(AHost, AIPVersion);
  301. end else begin
  302. LIP := AHost;
  303. end;
  304. end
  305. else
  306. begin //IPv6
  307. LIP := MakeCanonicalIPv6Address(AHost);
  308. if LIP = '' then begin //if MakeCanonicalIPv6Address failed, we have a hostname
  309. if Assigned(OnStatus) then begin
  310. DoStatus(hsResolving, [AHost]);
  311. end;
  312. LIP := GStack.ResolveHost(AHost, AIPVersion);
  313. end else begin
  314. LIP := AHost;
  315. end;
  316. end;
  317. }
  318. LIP := GStack.ResolveHost(AHost, AIPVersion);
  319. Binding.SendTo(LIP, APort, ABuffer, AIPVersion);
  320. end;
  321. procedure TIdUDPBase.SetActive(const Value: Boolean);
  322. begin
  323. if IsDesignTime or IsLoading then begin
  324. // don't activate at designtime (or during loading of properties) {Do not Localize}
  325. FDsgnActive := Value;
  326. end
  327. else if Active <> Value then begin
  328. if Value then begin
  329. GetBinding;
  330. end else begin
  331. CloseBinding;
  332. end;
  333. end;
  334. end;
  335. procedure TIdUDPBase.SetBroadcastEnabled(const AValue: Boolean);
  336. begin
  337. if FBroadCastEnabled <> AValue then begin
  338. FBroadcastEnabled := AValue;
  339. if Active then begin
  340. BroadcastEnabledChanged;
  341. end;
  342. end;
  343. end;
  344. procedure TIdUDPBase.SetHost(const AValue: String);
  345. begin
  346. FHost := Avalue;
  347. end;
  348. procedure TIdUDPBase.SetIPVersion(const AValue: TIdIPVersion);
  349. begin
  350. FIPVersion := AValue;
  351. end;
  352. procedure TIdUDPBase.SetPort(const AValue: TIdPort);
  353. begin
  354. FPort := AValue;
  355. end;
  356. end.