PageRenderTime 44ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/winshoes/Winshoes/UDPWinshoe.pas

https://code.google.com/p/moops-client/
Pascal | 320 lines | 253 code | 36 blank | 31 comment | 25 complexity | d3331ccd84c9451c981b40c195f9d313 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0
  1. unit UDPWinshoe;
  2. {
  3. 2000.02.03 - FIX Receive function BUG! By Victor.Ho
  4. 2000.01.13 - MTL
  5. - Moved to new Palette Scheme (Winshoes Clients and Winshoes Servers)
  6. 2000.01.05 - Kudzu
  7. - ReceiveTimeout property added
  8. - Renamed Receive to ReceiveWithTimeout and reimplemented Receive
  9. - Modified ReceiveWithTimeout to handle -1
  10. 1999.11.22 Addition Gregor Ibic, gregor.ibic@intelicom-sp.si
  11. - Added Timeout parameter to TWinshoeUDPClient which controls the timeout
  12. of the RecvFrom function. Is set different from 0 it set up to timeout
  13. after n milliseconds.
  14. 1999.11.15 Modification Gregor Ibic, gregor.ibic@intelicom-sp.si
  15. - Fixed the Active status bug. Now, if set in design time, UDPListener
  16. starts OK.
  17. }
  18. interface
  19. uses
  20. Classes
  21. , Messages
  22. , ThreadWinshoe
  23. , Windows, Winshoes, WinsockIntf;
  24. type
  25. TUDPReadEvent = procedure(Sender: TObject; const psData, psPeer: string; const piPort: Integer)
  26. of object;
  27. TWinshoeUDP = class(TWinshoe)
  28. protected
  29. FiUDPSize: Integer;
  30. fsUDPBuffer: string;
  31. //
  32. procedure CheckUDPBuffer;
  33. class procedure SendToPrimitive(pHandle: THandle; pAddr_Remote: TSockAddrIn; psData: string);
  34. procedure SetUDPSize(const iValue: Integer);
  35. public
  36. constructor Create(AOwner: TComponent); override;
  37. published
  38. property UDPSize: Integer read FiUDPSize write SetUDPSize;
  39. end;
  40. TWinshoeUDPListener = class;
  41. TWinshoeUDPListenerThread = class(TkdzuThread)
  42. protected
  43. fsData, fsPeer: string;
  44. fiPort: integer;
  45. public
  46. fListener: TWinshoeUDPListener;
  47. //
  48. procedure Run; override;
  49. procedure UDPRead;
  50. published
  51. end;
  52. TWinshoeUDPListener = class(TWinshoeUDP)
  53. protected
  54. fbActive: Boolean;
  55. fOnUDPRead: TUDPReadEvent;
  56. fthrdListener: TWinshoeUDPListenerThread;
  57. //
  58. procedure Loaded; override;
  59. procedure SetActive(const bValue: Boolean);
  60. public
  61. destructor Destroy; override;
  62. procedure DoUDPRead(const psData, psPeer: string; const piPort: Integer); virtual;
  63. procedure SendTo(const psIP: string; const piPort: Integer; const psData: string);
  64. published
  65. property Active: boolean read FbActive write SetActive default False;
  66. property OnUDPRead: TUDPReadEvent read FOnUDPRead write FOnUDPRead;
  67. end;
  68. TWinshoeUDPClient = class(TWinshoeUDP)
  69. protected
  70. fsHost, fsPeerAddress: String;
  71. fnReceiveTimeout: Integer;
  72. fAddr_Remote: TSockAddrin;
  73. public
  74. procedure Connect;
  75. constructor Create(anOwner: TComponent); override;
  76. function Receive: string;
  77. function ReceiveWithTimeout(const piMSec: Integer): string;
  78. function Readable(const piMSec: Integer): boolean;
  79. procedure Send(psData: string);
  80. //
  81. property PeerAddress: string read fsPeerAddress write fsPeerAddress;
  82. published
  83. property Host: string read fsHost write fsHost;
  84. property ReceiveTimeout: Integer read fnReceiveTimeout write fnReceiveTimeout;
  85. end;
  86. // Procs
  87. procedure Register;
  88. implementation
  89. uses
  90. GlobalWinshoe
  91. , SysUtils;
  92. procedure Register;
  93. begin
  94. RegisterComponents('Winshoes Clients', [TWinshoeUDPClient]);
  95. RegisterComponents('Winshoes Servers', [TWinshoeUDPListener]);
  96. end;
  97. procedure TWinshoeUDP.CheckUDPBuffer;
  98. begin
  99. // In case dynamically created
  100. if Length(fsUDPBuffer) = 0 then
  101. SetUDPSize(FiUDPSize);
  102. end;
  103. procedure TWinshoeUDPClient.Connect;
  104. begin
  105. if Connected then
  106. raise EWinshoeAlreadyConnected.Create('Already connected.');
  107. AllocateSocket(SOCK_DGRAM); try
  108. with fAddr_Remote do begin
  109. sin_family := PF_INET;
  110. sin_port := WinsockInterface.HToNS(Port);
  111. sin_addr.S_addr := ResolveHost(fsHost, fsPeerAddress);;
  112. end;
  113. except
  114. On E: Exception do begin
  115. Disconnect;
  116. raise;
  117. end;
  118. end;
  119. end;
  120. constructor TWinshoeUDPClient.Create(anOwner: TComponent);
  121. begin
  122. inherited;
  123. ReceiveTimeout := -1;
  124. end;
  125. function TWinshoeUDPClient.Readable;
  126. var
  127. tmTo: TTimeVal;
  128. FDRead: TFDSet;
  129. begin
  130. tmTo.tv_sec := piMSec div 1000;
  131. tmTo.tv_usec := piMSec mod 1000;
  132. FDRead.fd_count := 1;
  133. FDRead.fd_array[0] := Handle;
  134. if piMSec = -1 then begin
  135. Result := WinsockInterface.Select(0, @FDRead, nil, nil, nil) = 1
  136. end else begin
  137. Result := WinsockInterface.Select(0, @FDRead, nil, nil, @tmTO) = 1;
  138. {TODO - Split this up and do DoProcess}
  139. end;
  140. //NOTE - this is currently kind of a hack - there is a newer/better plan that I have to find time
  141. //to implement
  142. DoProcess;
  143. end;
  144. function TWinshoeUDPClient.Receive: string;
  145. begin
  146. Result:=ReceiveWithTimeout(ReceiveTimeout); //FIXED Victor.Ho
  147. end;
  148. function TWinshoeUDPClient.ReceiveWithTimeout(const piMSec: Integer): string;
  149. var
  150. i, iByteCount: Integer;
  151. AddrVoid: TSockAddrIn;
  152. begin
  153. CheckUDPBuffer;
  154. i := SizeOf(AddrVoid);
  155. if piMsec <> -1 then begin
  156. if not Readable(piMSec) then begin
  157. Result := '';
  158. exit;
  159. end;
  160. end;
  161. iByteCount := WinsockInterface.RecvFrom(Handle, fsUDPBuffer[1], Length(fsUDPBuffer), 0, AddrVoid
  162. , i);
  163. CheckForSocketError(iByteCount);
  164. if iByteCount = 0 then begin
  165. raise EWinshoeException.Create('Receive Error = 0.');
  166. end;
  167. result := Copy(fsUDPBuffer,1, iByteCount);
  168. end;
  169. procedure TWinshoeUDPClient.Send;
  170. begin
  171. SendToPrimitive(Handle, fAddr_Remote, psData);
  172. end;
  173. procedure TWinshoeUDPListener.Loaded;
  174. begin
  175. inherited Loaded;
  176. if Active then begin
  177. fbActive := False;
  178. SetActive(True);
  179. end;
  180. end;
  181. procedure TWinshoeUDPListener.SetActive;
  182. begin
  183. if fbActive = bValue then
  184. exit;
  185. if not ((csLoading in ComponentState) or (csDesigning in ComponentState)) then begin
  186. if bValue then begin
  187. CheckUDPBuffer;
  188. AllocateSocket(SOCK_DGRAM);
  189. Bind;
  190. //
  191. fthrdListener := TWinshoeUDPListenerThread.Create(True);
  192. fthrdListener.fListener := Self;
  193. fthrdListener.FreeOnTerminate := True;
  194. fthrdListener.Resume;
  195. end else begin
  196. // Necessary here - cancels the recvfrom in the listener thread
  197. Disconnect;
  198. // No listener is created if in design mode
  199. if fthrdListener <> nil then begin
  200. // Is Free on Terminate - dont free thread.
  201. fthrdListener.TerminateAndWaitFor;
  202. end;
  203. end;
  204. end;
  205. fbActive := bValue;
  206. end;
  207. constructor TWinshoeUDP.Create(AOwner: TComponent);
  208. begin
  209. inherited;
  210. fiUDPSize := WinsockInterface.MaxUDPSize;
  211. end;
  212. class procedure TWinshoeUDP.SendToPrimitive;
  213. var
  214. iBytesOut: Integer;
  215. begin
  216. iBytesOut := WinsockInterface.SendTo(pHandle, psData[1], Length(psData), 0, pAddr_Remote
  217. , sizeof(pAddr_Remote));
  218. if iBytesOut = 0 then begin
  219. raise Exception.Create('0 bytes were sent.')
  220. end else if iBytesOut = SOCKET_ERROR then begin
  221. if WinsockInterface.WSAGetLastError() = WSAEMSGSIZE then
  222. raise Exception.Create('Package Size Too Big')
  223. else
  224. CheckForSocketError(SOCKET_ERROR);
  225. end else if iBytesOut <> Length(psData) then begin
  226. raise Exception.Create('Not all bytes sent.');
  227. end;
  228. end;
  229. procedure TWinshoeUDP.SetUDPSize;
  230. begin
  231. if iValue > WinsockInterface.MaxUDPSize then
  232. raise EWinshoeException.Create('Max UDP size is: ' + IntToStr(WinsockInterface.MaxUDPSize));
  233. FiUDPSize := iValue;
  234. if not (csDesigning in ComponentState) then
  235. SetLength(fsUDPBuffer, fiUDPSize);
  236. end;
  237. destructor TWinshoeUDPListener.destroy;
  238. begin
  239. Active := False;
  240. inherited Destroy;
  241. end;
  242. procedure TWinshoeUDPListener.DoUDPRead(const psData, psPeer: string; const piPort: Integer);
  243. begin
  244. if assigned(OnUDPRead) then begin
  245. OnUDPRead(Self, psData, psPeer, piPort);
  246. end;
  247. end;
  248. procedure TWinshoeUDPListener.SendTo;
  249. var
  250. Addr_Remote: TSockAddrIn;
  251. begin
  252. addr_remote.sin_family := PF_INET;
  253. addr_remote.sin_port := WinsockInterface.htons(piPort);
  254. addr_remote.sin_addr.S_addr := TWinshoe.ResolveHost(psIP, sVoid);
  255. SendToPrimitive(Handle, addr_remote, psData);
  256. end;
  257. procedure TWinshoeUDPListenerThread.Run;
  258. var
  259. i, iByteCount: Integer;
  260. addr_remote: TSockAddrin;
  261. begin
  262. i := SizeOf(addr_remote);
  263. iByteCount := WinsockInterface.RecvFrom(fListener.Handle, fListener.fsUDPBuffer[1]
  264. , Length(fListener.fsUDPBuffer), 0, addr_remote, i);
  265. // Thread may be terminated
  266. fListener.CheckForSocketError2(iByteCount, [10004, 10038]);
  267. // Thread may be terminated
  268. if fListener.Connected then begin
  269. if iByteCount = 0 then begin
  270. raise EWinshoeException.Create('RecvFrom Error = 0.');
  271. end;
  272. fsData := Copy(fListener.fsUDPBuffer, 1, iByteCount);
  273. fsPeer := String(TWinshoe.TInAddrToString(addr_remote.sin_addr));
  274. fiPort := WinsockInterface.NToHS(addr_remote.sin_port);
  275. Synchronize(UDPRead);
  276. end else begin
  277. Terminate;
  278. end;
  279. end;
  280. procedure TWinshoeUDPListenerThread.UDPRead;
  281. begin
  282. fListener.DoUDPRead(fsData, fsPeer, fiPort);
  283. end;
  284. end.