fpwebsocketclient.pp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702
  1. {
  2. $Id: header,v 1.1 2000/07/13 06:33:45 michael Exp $
  3. This file is part of the Free Component Library (FCL)
  4. Copyright (c) 2021 - by the Free Pascal development team
  5. Websocket client implementation.
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. **********************************************************************}
  12. {$mode ObjFPC}{$H+}
  13. unit fpwebsocketclient;
  14. interface
  15. uses
  16. sysutils, classes, fpwebsocket, ssockets;
  17. Type
  18. EWebSocketClient = Class(EWebSocket);
  19. TWSClientHandShakeEvent = Procedure(Sender : TObject; aHeaders : TStrings) of Object;
  20. TWSClientHandShakeResponseEvent = Procedure(Sender : TObject; aResponse : TWSHandShakeResponse; Var aAllow : Boolean) of Object;
  21. TWSErrorEvent = Procedure (Sender : TObject; E : Exception) of object;
  22. { TWSMessagePump }
  23. TWSMessagePump = Class (TComponent)
  24. private
  25. FInterval:Integer;
  26. FList: TThreadList;
  27. FReads: TSocketStreamArray;
  28. FExceptions : TSocketStreamArray;
  29. FOnError: TWSErrorEvent;
  30. procedure SetInterval(AValue: Integer);
  31. Protected
  32. function WaitForData: Boolean;
  33. Function CheckConnections : Boolean; virtual;
  34. Procedure ReadConnections;
  35. Property List : TThreadList Read FList;
  36. Public
  37. Constructor Create(aOwner : TComponent); override;
  38. Destructor Destroy; override;
  39. Procedure AddClient(aConnection : TWSClientConnection);
  40. Procedure RemoveClient(aConnection : TWSClientConnection);
  41. Procedure Execute; virtual; abstract;
  42. Procedure Terminate; virtual; abstract;
  43. Property Interval : Integer Read FInterval Write SetInterval;
  44. Property OnError : TWSErrorEvent Read FOnError Write FOnError;
  45. End;
  46. // Default message driver, works with thread that checks sockets for available data
  47. TWSThreadMessagePump = Class(TWSMessagePump)
  48. Private
  49. FThread : TThread;
  50. Procedure ThreadTerminated(Sender : TObject);
  51. Protected
  52. Type
  53. TMessageDriverThread = Class(TThread)
  54. Public
  55. FPump : TWSThreadMessagePump;
  56. Constructor Create(aPump : TWSThreadMessagePump; aTerminate : TNotifyEvent);
  57. Procedure Execute;override;
  58. End;
  59. Public
  60. Procedure Execute; override;
  61. Procedure Terminate; override;
  62. End;
  63. TCustomWebsocketClient = class;
  64. { TWebSocketClientConnection }
  65. TWebSocketClientConnection = class(TWSClientConnection)
  66. protected
  67. Procedure DoDisconnect; override;
  68. function GetClient: TCustomWebsocketClient; virtual;
  69. Public
  70. Property WebsocketClient : TCustomWebsocketClient Read GetClient;
  71. end;
  72. { TCustomWSClientConnection }
  73. { TCustomWebsocketClient }
  74. TCustomWebsocketClient = Class(TComponent)
  75. private
  76. FOutGoingFrameMask: Integer;
  77. FPort: Integer;
  78. FActive: Boolean;
  79. FLoadActive : Boolean;
  80. FHostName: String;
  81. FUseSSL: Boolean;
  82. FResource: string;
  83. FConnectTimeout: Integer;
  84. FOptions: TWSOptions;
  85. FSocket : TInetSocket;
  86. FTransport : TWSClientTransport;
  87. FCheckTimeOut: Integer;
  88. FAutoCheckMessages: Boolean;
  89. FHandShake : TWSHandShakeRequest;
  90. FMessagePump: TWSMessagePump; // Do not free
  91. FHandshakeResponse: TWSHandShakeResponse;
  92. FOnSendHandShake: TWSClientHandshakeEvent;
  93. FOnHandshakeResponse: TWSClientHandshakeResponseEvent;
  94. FConnection: TWebSocketClientConnection;
  95. FOnMessageReceived: TWSMessageEvent;
  96. FOnControl: TWSControlEvent;
  97. FOnDisconnect: TNotifyEvent;
  98. FOnConnect: TNotifyEvent;
  99. procedure SetActive(const Value: Boolean);
  100. procedure SetHostName(const Value: String);
  101. procedure SetMessagePump(AValue: TWSMessagePump);
  102. procedure SetPort(const Value: Integer);
  103. procedure SetUseSSL(const Value: Boolean);
  104. procedure SetConnectTimeout(const Value: Integer);
  105. procedure SetResource(const Value: string);
  106. procedure SetCheckTimeOut(const Value: Integer);
  107. procedure SetOptions(const Value: TWSOptions);
  108. procedure SetAutoCheckMessages(const Value: Boolean);
  109. procedure SendHeaders(aHeaders: TStrings);
  110. procedure ConnectionDisconnected(Sender: TObject);
  111. Protected
  112. Procedure CheckInactive;
  113. Procedure Loaded; override;
  114. function CreateClientConnection(aTransport : TWSClientTRansport): TWebSocketClientConnection; virtual;
  115. procedure MessageReceived(Sender: TObject; const aMessage : TWSMessage);
  116. Procedure ControlReceived(Sender: TObject; aType : TFrameType; const aData: TBytes);virtual;
  117. function CheckHandShakeResponse(aHeaders: TStrings): Boolean; virtual;
  118. function CreateHandShakeRequest: TWSHandShakeRequest; virtual;
  119. function CreateHandshakeResponse(aHeaders: TStrings): TWSHandShakeResponse; virtual;
  120. procedure SendHandShakeRequest; virtual;
  121. function ReadHandShakeResponse: Boolean; virtual;
  122. Function DoHandShake : Boolean;
  123. Property Transport : TWSClientTRansport Read FTransport;
  124. Public
  125. Property Connection: TWebSocketClientConnection Read FConnection;
  126. Public
  127. Destructor Destroy; override;
  128. // Check for incoming messages
  129. Function CheckIncoming : TIncomingResult;
  130. // Connect and perform handshake
  131. Procedure Connect;
  132. // Disconnect from server.
  133. Procedure Disconnect(SendClose : boolean = true);
  134. // Send a ping message
  135. Procedure Ping(aMessage: UTF8String);
  136. // Send a pong message
  137. Procedure Pong(aMessage: UTF8String);
  138. // Send raw data (ftBinary)
  139. Procedure SendData(aBytes : TBytes);
  140. // Send a string message
  141. Procedure SendMessage(Const aMessage : String);
  142. Public
  143. // Connect/Disconnect
  144. Property Active : Boolean Read FActive Write SetActive;
  145. // Check for message timeout
  146. Property CheckTimeOut : Integer Read FCheckTimeOut Write SetCheckTimeOut;
  147. // Timeout for connect
  148. Property ConnectTimeout : Integer Read FConnectTimeout Write SetConnectTimeout;
  149. // Host to connect to
  150. Property HostName : String Read FHostName Write SetHostName;
  151. // Message driver
  152. Property MessagePump : TWSMessagePump Read FMessagePump Write SetMessagePump;
  153. // Options
  154. Property Options : TWSOptions Read FOptions Write SetOptions;
  155. // Mask to use for outgoing frames
  156. Property OutGoingFrameMask : Integer Read FOutGoingFrameMask Write FOutGoingFrameMask;
  157. // Port to connect to
  158. Property Port : Integer Read FPort Write SetPort;
  159. // Path/Document in HTTP URL for GET request
  160. Property Resource : string Read FResource Write SetResource;
  161. // User SSL when connecting
  162. Property UseSSL : Boolean Read FUseSSL Write SetUseSSL;
  163. // Events
  164. // Called when handshake is about to be sent
  165. Property OnSendHandShake : TWSClientHandshakeEvent Read FOnSendHandShake Write FOnSendHandshake;
  166. // Called when handshake response is received
  167. Property OnHandshakeResponse : TWSClientHandshakeResponseEvent Read FOnHandshakeResponse Write FOnHandshakeResponse;
  168. // Called when a text message is received.
  169. property OnMessageReceived: TWSMessageEvent read FOnMessageReceived write FOnMessageReceived;
  170. // Called when a connection is disconnected. Sed
  171. property OnDisconnect: TNotifyEvent read FOnDisconnect write FOnDisconnect;
  172. // Called when a connection is established
  173. property OnConnect: TNotifyEvent read FOnConnect write FOnConnect;
  174. // Called when a control message is received.
  175. property OnControl: TWSControlEvent read FOnControl write FOnControl;
  176. End;
  177. TWebsocketClient = Class(TCustomWebsocketClient)
  178. Published
  179. Property HostName;
  180. Property Port;
  181. Property CheckTimeOut;
  182. Property ConnectTimeout;
  183. Property MessagePump;
  184. Property Options;
  185. Property Resource;
  186. Property UseSSL;
  187. Property OnSendHandShake;
  188. Property OnHandshakeResponse;
  189. property OnMessageReceived;
  190. property OnDisconnect;
  191. property OnConnect;
  192. property OnControl;
  193. Property OutGoingFrameMask;
  194. End;
  195. implementation
  196. uses sha1;
  197. { TWebSocketClientConnection }
  198. procedure TWebSocketClientConnection.DoDisconnect;
  199. begin
  200. If Assigned(WebSocketClient) then
  201. WebSocketClient.ConnectionDisconnected(Self);
  202. end;
  203. function TWebSocketClientConnection.GetClient: TCustomWebsocketClient;
  204. begin
  205. Result:=Owner as TCustomWebsocketClient;
  206. end;
  207. { TCustomWebsocketClient }
  208. procedure TCustomWebsocketClient.CheckInactive;
  209. begin
  210. If Active then
  211. Raise EWebSocketClient.Create(SErrConnectionActive);
  212. end;
  213. Function TCustomWebsocketClient.CheckIncoming : TIncomingResult;
  214. begin
  215. If Not Active then
  216. Raise EWebSocketClient.Create(SErrConnectionInActive);
  217. if Not Connection.HandshakeCompleted then
  218. Raise EWebSocketClient.Create(SErrHandshakeInComplete);
  219. Result:=Connection.CheckIncoming(CheckTimeout);
  220. if (Result=irClose) then
  221. begin
  222. Disconnect(False);
  223. end;
  224. end;
  225. procedure TCustomWebsocketClient.ControlReceived(Sender: TObject; aType : TFrameType; const aData: TBytes);
  226. begin
  227. If Assigned(FOnControl) then
  228. FOnControl(Sender, aType, aData);
  229. end;
  230. function TCustomWebsocketClient.CreateClientConnection(aTransport: TWSClientTRansport): TWebsocketClientConnection;
  231. begin
  232. Result:=TWebsocketClientConnection.Create(Self,aTransport,FOptions);
  233. end;
  234. procedure TCustomWebsocketClient.ConnectionDisconnected(Sender : TObject);
  235. begin
  236. Factive:=False;
  237. If Assigned(MessagePump) then
  238. MessagePump.RemoveClient(FConnection);
  239. If Assigned(OnDisconnect) then
  240. OnDisconnect(FConnection);
  241. end;
  242. procedure TCustomWebsocketClient.Connect;
  243. begin
  244. If Active then
  245. Exit;
  246. FreeAndNil(FSocket);
  247. FreeAndNil(FTransport);
  248. FSocket:=TInetSocket.Create(HostName,Port,ConnectTimeout);
  249. FTransport:=TWSClientTransport.Create(FSocket);
  250. FConnection:=CreateClientConnection(FTransport);
  251. FConnection.OnMessageReceived:=@MessageReceived;
  252. FConnection.OnControl:=@ControlReceived;
  253. FCOnnection.OutgoingFrameMask:=Self.OutGoingFrameMask;
  254. FActive:=True;
  255. if not DoHandShake then
  256. Disconnect(False)
  257. else
  258. begin
  259. If Assigned(MessagePump) then
  260. MessagePump.AddClient(FConnection);
  261. if Assigned(OnConnect) then
  262. OnConnect(Self);
  263. end;
  264. end;
  265. destructor TCustomWebsocketClient.Destroy;
  266. begin
  267. DisConnect(False);
  268. FreeAndNil(FHandShake);
  269. FreeAndNil(FHandshakeResponse);
  270. Inherited;
  271. end;
  272. Function TCustomWebsocketClient.CreateHandShakeRequest : TWSHandShakeRequest;
  273. begin
  274. Result:=TWSHandShakeRequest.Create('',Nil);
  275. end;
  276. procedure TCustomWebsocketClient.SendData(aBytes: TBytes);
  277. begin
  278. Connection.Send(aBytes);
  279. end;
  280. procedure TCustomWebsocketClient.SendHeaders(aHeaders : TStrings);
  281. Var
  282. S : String;
  283. B : TBytes;
  284. begin
  285. for S in AHeaders do
  286. begin
  287. B:=TEncoding.UTF8.GetAnsiBytes(S+#13#10);
  288. Connection.Transport.WriteBytes(B,Length(B));
  289. end;
  290. B:=TEncoding.UTF8.GetAnsiBytes(#13#10);
  291. Connection.Transport.WriteBytes(B,Length(B));
  292. end;
  293. procedure TCustomWebsocketClient.SendHandShakeRequest;
  294. Var
  295. aRequest : TWSHandShakeRequest;
  296. aHeaders : TStrings;
  297. begin
  298. aHeaders:=Nil;
  299. FreeAndNil(FHandShake);
  300. aRequest:=CreateHandShakeRequest;
  301. try
  302. aRequest.Host:=HostName;
  303. aRequest.Port:=Port;
  304. aRequest.Resource:=Resource;
  305. aHeaders:=TStringList.Create;
  306. aHeaders.NameValueSeparator:=':';
  307. aRequest.ToStrings(aHeaders);
  308. if Assigned(FOnSendHandshake) then
  309. FOnSendHandshake(self,aHeaders);
  310. // Do not use FClient.WriteHeader, it messes up the strings !
  311. SendHeaders(aHeaders);
  312. FHandShake:=aRequest;
  313. finally
  314. aHeaders.Free;
  315. if FhandShake<>aRequest then
  316. aRequest.Free;
  317. end;
  318. end;
  319. procedure TCustomWebsocketClient.SendMessage(const aMessage: String);
  320. begin
  321. Connection.Send(aMessage);
  322. end;
  323. Function TCustomWebsocketClient.CreateHandshakeResponse(aHeaders : TStrings) : TWSHandShakeResponse;
  324. begin
  325. Result:=TWSHandShakeResponse.Create('',aHeaders);
  326. end;
  327. Function TCustomWebsocketClient.CheckHandShakeResponse(aHeaders : TStrings) : Boolean;
  328. Var
  329. K : String;
  330. hash : TSHA1Digest;
  331. B : TBytes;
  332. begin
  333. B:=[];
  334. FreeAndNil(FHandshakeResponse);
  335. FHandshakeResponse:=CreateHandshakeResponse(aHeaders);
  336. k := Trim(FHandshake.Key) + SSecWebSocketGUID;
  337. hash:=sha1.SHA1String(k);
  338. SetLength(B,SizeOf(hash));
  339. Move(Hash,B[0],Length(B));
  340. k:=EncodeBytesBase64(B);
  341. Result:=SameText(K,FHandshakeResponse.Accept)
  342. and SameText(FHandshakeResponse.Upgrade,'websocket');
  343. end;
  344. Function TCustomWebsocketClient.ReadHandShakeResponse : Boolean;
  345. Var
  346. S : String;
  347. aHeaders : TStrings;
  348. begin
  349. Result:=False;
  350. aHeaders:=TStringList.Create;
  351. Try
  352. aHeaders.NameValueSeparator:=':';
  353. Repeat
  354. S:=Connection.Transport.ReadLn;
  355. aHeaders.Add(S);
  356. Until (S='');
  357. Result:=CheckHandShakeResponse(aHeaders);
  358. if Result and Assigned(FOnHandshakeResponse) then
  359. FOnHandshakeResponse(Self,FHandShakeResponse,Result);
  360. if Result then
  361. FConnection.HandshakeResponse:=FHandShakeResponse
  362. Finally
  363. aHeaders.Free;
  364. End;
  365. end;
  366. Function TCustomWebsocketClient.DoHandShake : Boolean;
  367. begin
  368. SendHandShakeRequest;
  369. Result:=ReadHandShakeResponse;
  370. end;
  371. procedure TCustomWebsocketClient.Loaded;
  372. begin
  373. inherited;
  374. if FLoadActive then
  375. Connect;
  376. end;
  377. procedure TCustomWebsocketClient.MessageReceived(Sender: TObject; const aMessage : TWSMessage) ;
  378. begin
  379. if Assigned(OnMessageReceived) and (TWSClientConnection(Sender).HandshakeCompleted) then
  380. OnMessageReceived(Self, AMessage);
  381. end;
  382. procedure TCustomWebsocketClient.Ping(aMessage: UTF8String);
  383. begin
  384. FConnection.Send(ftPing,TEncoding.UTF8.GetAnsiBytes(aMessage));
  385. end;
  386. procedure TCustomWebsocketClient.Pong(aMessage: UTF8String);
  387. begin
  388. FConnection.Send(ftPong,TEncoding.UTF8.GetAnsiBytes(aMessage));
  389. end;
  390. procedure TCustomWebsocketClient.Disconnect(SendClose : boolean = true);
  391. begin
  392. if Not Active then
  393. Exit;
  394. if SendClose then
  395. Connection.Close('');
  396. if Assigned(MessagePump) then
  397. MessagePump.RemoveClient(Connection);
  398. FreeAndNil(FConnection);
  399. FActive:=False;
  400. end;
  401. procedure TCustomWebsocketClient.SetActive(const Value: Boolean);
  402. begin
  403. FLoadActive := Value;
  404. if (csDesigning in ComponentState) then
  405. exit;
  406. if Value then
  407. Connect
  408. else
  409. Disconnect;
  410. end;
  411. procedure TCustomWebsocketClient.SetAutoCheckMessages(const Value: Boolean);
  412. begin
  413. CheckInactive;
  414. FAutoCheckMessages := Value;
  415. end;
  416. procedure TCustomWebsocketClient.SetCheckTimeOut(const Value: Integer);
  417. begin
  418. CheckInactive;
  419. FCheckTimeOut := Value;
  420. end;
  421. procedure TCustomWebsocketClient.SetConnectTimeout(const Value: Integer);
  422. begin
  423. CheckInactive;
  424. FConnectTimeout := Value;
  425. end;
  426. procedure TCustomWebsocketClient.SetHostName(const Value: String);
  427. begin
  428. CheckInactive;
  429. FHostName := Value;
  430. end;
  431. procedure TCustomWebsocketClient.SetMessagePump(AValue: TWSMessagePump);
  432. begin
  433. if FMessagePump=AValue then Exit;
  434. If Assigned(FMessagePump) then
  435. FMessagePump.RemoveFreeNotification(Self);
  436. FMessagePump:=AValue;
  437. If Assigned(FMessagePump) then
  438. FMessagePump.FreeNotification(Self);
  439. end;
  440. procedure TCustomWebsocketClient.SetOptions(const Value: TWSOptions);
  441. begin
  442. CheckInactive;
  443. FOptions := Value;
  444. end;
  445. procedure TCustomWebsocketClient.SetPort(const Value: Integer);
  446. begin
  447. CheckInactive;
  448. FPort := Value;
  449. end;
  450. procedure TCustomWebsocketClient.SetResource(const Value: string);
  451. begin
  452. CheckInactive;
  453. FResource := Value;
  454. end;
  455. procedure TCustomWebsocketClient.SetUseSSL(const Value: Boolean);
  456. begin
  457. CheckInactive;
  458. FUseSSL := Value;
  459. end;
  460. { TTMSClientWebSocketConnection }
  461. { TWSMessagePump }
  462. procedure TWSMessagePump.AddClient(aConnection: TWSClientConnection);
  463. begin
  464. List.Add(aConnection);
  465. end;
  466. procedure TWSMessagePump.RemoveClient(aConnection: TWSClientConnection);
  467. begin
  468. FList.Remove(aConnection);
  469. end;
  470. procedure TWSMessagePump.SetInterval(AValue: Integer);
  471. begin
  472. if FInterval=AValue then Exit;
  473. FInterval:=AValue;
  474. end;
  475. Function TWSMessagePump.WaitForData : Boolean;
  476. Var
  477. dummy1,dummy2 : TSocketStreamArray;
  478. begin
  479. Dummy1:=Nil;
  480. Dummy2:=Nil;
  481. Result:=False;
  482. // FReadSet was populated by checkconnections
  483. SetLength(FExceptions,0);
  484. if Length(FReads)=0 then
  485. begin
  486. TThread.Sleep(FInterval);
  487. end
  488. else
  489. begin
  490. try
  491. // We take the first ont in the list.
  492. Result := FReadS[0].Select(FReads,dummy1,dummy2,FInterval);
  493. except
  494. Result := False;
  495. end;
  496. end;
  497. end;
  498. function TWSMessagePump.CheckConnections: Boolean;
  499. Var
  500. aList : TList;
  501. aClient: TWSClientConnection;
  502. aTrans : TWSClientTransport;
  503. I,aLen : Integer;
  504. begin
  505. Result:=False;
  506. aList := List.LockList;
  507. try
  508. aLen:=0;
  509. SetLength(FReads,aList.Count);
  510. for I := 0 to aList.Count - 1 do
  511. begin
  512. aClient := TWSClientConnection(aList.Items[I]);
  513. if assigned(aClient) then
  514. aTrans:=aClient.ClientTransport
  515. else
  516. aTrans:=Nil;
  517. if (aTrans<>nil) then
  518. begin
  519. // There is already data
  520. FReads[aLen]:=aTrans.Socket;
  521. Inc(aLen);
  522. end;
  523. end;
  524. finally
  525. List.UnlockList;
  526. end;
  527. if Not Result then
  528. Result:=WaitForData;
  529. end;
  530. constructor TWSMessagePump.Create(aOwner : TComponent);
  531. begin
  532. FList:=TThreadList.Create;
  533. FReads:=[];
  534. FExceptions:=[];
  535. Finterval:=50;
  536. end;
  537. destructor TWSMessagePump.Destroy;
  538. begin
  539. FreeAndNil(FList);
  540. inherited;
  541. end;
  542. procedure TWSMessagePump.ReadConnections;
  543. Var
  544. aList : TList;
  545. aClient: TWSClientConnection;
  546. I : Integer;
  547. begin
  548. try
  549. aList := List.LockList;
  550. try
  551. FReads:=[];
  552. for I := 0 to aList.Count - 1 do
  553. begin
  554. aClient:= TWSClientConnection(aList.Items[I]);
  555. if assigned(aClient.Transport) then
  556. aClient.CheckIncoming(1);
  557. end;
  558. finally
  559. List.UnlockList;
  560. end;
  561. except
  562. on E: Exception do
  563. if Assigned(OnError) then
  564. OnError(Self,E);
  565. end;
  566. end;
  567. { TWSThreadMessagePump }
  568. procedure TWSThreadMessagePump.Execute;
  569. begin
  570. FThread:=TMessageDriverThread.Create(Self,@ThreadTerminated);
  571. end;
  572. procedure TWSThreadMessagePump.ThreadTerminated(Sender: TObject);
  573. begin
  574. FThread:=Nil;
  575. end;
  576. procedure TWSThreadMessagePump.Terminate;
  577. begin
  578. FThread.Terminate;
  579. if Assigned(FThread) then
  580. FThread.WaitFor;
  581. end;
  582. { TWSThreadMessagePump.TMessageDriverThread }
  583. constructor TWSThreadMessagePump.TMessageDriverThread.Create(aPump: TWSThreadMessagePump; aTerminate : TNotifyEvent);
  584. begin
  585. FPump:=aPump;
  586. OnTerminate:=aTerminate;
  587. Inherited Create(False);
  588. end;
  589. procedure TWSThreadMessagePump.TMessageDriverThread.Execute;
  590. begin
  591. While Not Terminated do
  592. if FPump.CheckConnections then
  593. FPump.ReadConnections
  594. else
  595. TThread.Sleep(FPump.Interval);
  596. end;
  597. end.