IdIPMCastClient.pas 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  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: 10221: IdIPMCastClient.pas
  11. {
  12. { Rev 1.0 2002.11.12 10:43:14 PM czhower
  13. }
  14. unit IdIPMCastClient;
  15. interface
  16. uses
  17. Classes,
  18. IdIPMCastBase, IdUDPBase, IdComponent, IdSocketHandle, IdThread, IdException;
  19. const
  20. DEF_IMP_THREADEDEVENT = False;
  21. type
  22. TIPMCastReadEvent = procedure(Sender: TObject; AData: TStream; ABinding: TIdSocketHandle) of object;
  23. TIdIPMCastClient = class;
  24. TIdIPMCastListenerThread = class(TIdThread)
  25. protected
  26. IncomingData: TIdSocketHandle;
  27. FAcceptWait: integer;
  28. FBuffer: TMemoryStream;
  29. FBufferSize: integer;
  30. public
  31. FServer: TIdIPMCastClient;
  32. //
  33. constructor Create(Owner: TIdIPMCastClient); reintroduce;
  34. destructor Destroy; override;
  35. procedure Run; override;
  36. procedure IPMCastRead;
  37. //
  38. property AcceptWait: integer read FAcceptWait write FAcceptWait;
  39. published
  40. end;
  41. TIdIPMCastClient = class(TIdIPMCastBase)
  42. protected
  43. FBindings: TIdSocketHandles;
  44. FBufferSize: Integer;
  45. FCurrentBinding: TIdSocketHandle;
  46. FListenerThread: TIdIPMCastListenerThread;
  47. FOnIPMCastRead: TIPMCastReadEvent;
  48. FThreadedEvent: boolean;
  49. //
  50. procedure CloseBinding; override;
  51. procedure DoIPMCastRead(AData: TStream; ABinding: TIdSocketHandle); virtual;
  52. function GetActive: Boolean; override;
  53. function GetBinding: TIdSocketHandle; override;
  54. function GetDefaultPort: integer;
  55. procedure PacketReceived(AData: TStream; ABinding: TIdSocketHandle);
  56. procedure SetBindings(const Value: TIdSocketHandles);
  57. procedure SetDefaultPort(const AValue: integer);
  58. public
  59. constructor Create(AOwner: TComponent); override;
  60. destructor Destroy; override;
  61. //
  62. published
  63. property Active;
  64. property Bindings: TIdSocketHandles read FBindings write SetBindings;
  65. property BufferSize: Integer read FBufferSize write FBufferSize default ID_UDP_BUFFERSIZE;
  66. property DefaultPort: integer read GetDefaultPort write SetDefaultPort;
  67. property MulticastGroup;
  68. property OnIPMCastRead: TIPMCastReadEvent read FOnIPMCastRead write FOnIPMCastRead;
  69. property ThreadedEvent: boolean read FThreadedEvent write FThreadedEvent default DEF_IMP_THREADEDEVENT;
  70. end;
  71. implementation
  72. uses
  73. IdResourceStrings, IdStack, IdStackConsts, IdGlobal, SysUtils;
  74. { TIdIPMCastClient }
  75. constructor TIdIPMCastClient.Create(AOwner: TComponent);
  76. begin
  77. inherited Create(AOwner);
  78. BufferSize := ID_UDP_BUFFERSIZE;
  79. FThreadedEvent := DEF_IMP_THREADEDEVENT;
  80. FBindings := TIdSocketHandles.Create(Self);
  81. end;
  82. procedure TIdIPMCastClient.CloseBinding;
  83. var
  84. i: integer;
  85. Multicast : TMultiCast;
  86. begin
  87. if Assigned(FCurrentBinding) then begin
  88. // Necessary here - cancels the recvfrom in the listener thread
  89. FListenerThread.Stop;
  90. for i := 0 to Bindings.Count - 1 do begin
  91. Multicast.IMRMultiAddr := GStack.StringToTInAddr(FMulticastGroup);
  92. Multicast.IMRInterface.S_addr := Id_INADDR_ANY;
  93. Bindings[i].SetSockOpt(Id_IPPROTO_IP, Id_IP_DROP_MEMBERSHIP, pchar(@Multicast), SizeOf(Multicast));
  94. Bindings[i].CloseSocket;
  95. end;
  96. FListenerThread.WaitFor;
  97. FreeAndNil(FListenerThread);
  98. FCurrentBinding := nil;
  99. end;
  100. end;
  101. procedure TIdIPMCastClient.DoIPMCastRead(AData: TStream; ABinding: TIdSocketHandle);
  102. begin
  103. if Assigned(OnIPMCastRead) then begin
  104. OnIPMCastRead(Self, AData, ABinding);
  105. end;
  106. end;
  107. function TIdIPMCastClient.GetActive: Boolean;
  108. begin
  109. // inherited GetActive keeps track of design-time Active property
  110. Result := inherited GetActive or
  111. (Assigned(FCurrentBinding) and FCurrentBinding.HandleAllocated);
  112. end;
  113. function TIdIPMCastClient.GetBinding: TIdSocketHandle;
  114. var
  115. i: integer;
  116. Multicast : TMultiCast;
  117. begin
  118. if not Assigned(FCurrentBinding) then
  119. begin
  120. if Bindings.Count < 1 then begin
  121. if DefaultPort > 0 then begin
  122. Bindings.Add;
  123. end else begin
  124. raise EIdMCastNoBindings.Create(RSNoBindingsSpecified);
  125. end;
  126. end;
  127. for i := 0 to Bindings.Count - 1 do begin
  128. Bindings[i].AllocateSocket(Id_SOCK_DGRAM);
  129. Bindings[i].Bind;
  130. Multicast.IMRMultiAddr := GStack.StringToTInAddr(FMulticastGroup);
  131. Multicast.IMRInterface.S_addr := Id_INADDR_ANY;
  132. Bindings[i].SetSockOpt(Id_IPPROTO_IP, Id_IP_ADD_MEMBERSHIP, pchar(@Multicast), SizeOf(Multicast));
  133. end;
  134. FCurrentBinding := Bindings[0];
  135. FListenerThread := TIdIPMCastListenerThread.Create(Self);
  136. FListenerThread.Start;
  137. end;
  138. Result := FCurrentBinding;
  139. end;
  140. function TIdIPMCastClient.GetDefaultPort: integer;
  141. begin
  142. result := FBindings.DefaultPort;
  143. end;
  144. procedure TIdIPMCastClient.PacketReceived(AData: TStream; ABinding: TIdSocketHandle);
  145. begin
  146. FCurrentBinding := ABinding;
  147. DoIPMCastRead(AData, ABinding);
  148. end;
  149. procedure TIdIPMCastClient.SetBindings(const Value: TIdSocketHandles);
  150. begin
  151. FBindings.Assign(Value);
  152. end;
  153. procedure TIdIPMCastClient.SetDefaultPort(const AValue: integer);
  154. begin
  155. if (FBindings.DefaultPort <> AValue) then begin
  156. FBindings.DefaultPort := AValue;
  157. FPort := AValue;
  158. end;
  159. end;
  160. destructor TIdIPMCastClient.Destroy;
  161. begin
  162. Active := False;
  163. FreeAndNil(FBindings);
  164. inherited Destroy;
  165. end;
  166. { TIdIPMCastListenerThread }
  167. constructor TIdIPMCastListenerThread.Create(Owner: TIdIPMCastClient);
  168. begin
  169. inherited Create(True);
  170. FAcceptWait := 1000;
  171. FBuffer := TMemoryStream.Create;
  172. FBufferSize := Owner.BufferSize;
  173. FServer := Owner;
  174. end;
  175. destructor TIdIPMCastListenerThread.Destroy;
  176. begin
  177. FreeAndNil(FBuffer);
  178. inherited Destroy;
  179. end;
  180. procedure TIdIPMCastListenerThread.Run;
  181. var
  182. PeerIP: string;
  183. i: Integer;
  184. PeerPort: Integer;
  185. ByteCount: Integer;
  186. LReadList: TList;
  187. begin
  188. // fill list of socket handles
  189. LReadList := TList.Create;
  190. try
  191. LReadList.Capacity := FServer.Bindings.Count;
  192. for i := 0 to FServer.Bindings.Count - 1 do begin
  193. LReadList.Add(Pointer(FServer.Bindings[i].Handle));
  194. end;
  195. GStack.WSSelect(LReadList, nil, nil, AcceptWait);
  196. for i := 0 to LReadList.Count - 1 do
  197. // Doublecheck to see if we've been stopped {Do not Localize}
  198. // Depending on timing - may not reach here if it is in ancestor run when thread is stopped
  199. if not Stopped then
  200. begin
  201. IncomingData := FServer.Bindings.BindingByHandle(TIdStackSocketHandle(LReadList[i]));
  202. FBuffer.SetSize(FBufferSize);
  203. ByteCount := GStack.WSRecvFrom(IncomingData.Handle, FBuffer.Memory^, FBufferSize, 0
  204. , PeerIP, PeerPort);
  205. GStack.CheckForSocketError(ByteCount);
  206. if ByteCount = 0 then
  207. begin
  208. raise EIdUDPReceiveErrorZeroBytes.Create(RSUDPReceiveError0);
  209. end;
  210. FBuffer.SetSize(ByteCount);
  211. //Some streams alter their position on SetSize
  212. FBuffer.Position := 0;
  213. IncomingData.SetPeer(PeerIP, PeerPort);
  214. if FServer.ThreadedEvent then
  215. begin
  216. IPMCastRead;
  217. end
  218. else begin
  219. Synchronize(IPMCastRead);
  220. end;
  221. end;
  222. finally
  223. LReadList.Free;
  224. end;
  225. end;
  226. procedure TIdIPMCastListenerThread.IPMCastRead;
  227. begin
  228. FServer.PacketReceived(FBuffer, IncomingData);
  229. end;
  230. end.