customserver2.pas 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801
  1. {==============================================================================|
  2. | Project : Bauglir Internet Library |
  3. |==============================================================================|
  4. | Content: Generic connection and server |
  5. |==============================================================================|
  6. | Copyright (c)2011-2012, Bronislav Klucka |
  7. | All rights reserved. |
  8. | Source code is licenced under original 4-clause BSD licence: |
  9. | http://licence.bauglir.com/bsd4.php |
  10. | |
  11. | |
  12. |==============================================================================|
  13. | Requirements: Ararat Synapse (http://www.ararat.cz/synapse/) |
  14. |==============================================================================}
  15. unit CustomServer2;
  16. {$IFDEF FPC}
  17. {$MODE DELPHI}
  18. {$ENDIF}
  19. {$H+}
  20. interface
  21. uses
  22. {$IFDEF UNIX}
  23. cthreads,
  24. {$ENDIF}
  25. Classes, SysUtils, blcksock, syncobjs, Sockets, ssl_openssl, BClasses;
  26. type
  27. TCustomServer = class;
  28. TCustomConnection = class;
  29. {:abstract(Socket used for @link(TCustomConnection)) }
  30. TTCPCustomConnectionSocket = class(TTCPBlockSocket)
  31. protected
  32. fConnection: TCustomConnection;
  33. fCurrentStatusReason: THookSocketReason;
  34. fCurrentStatusValue: string;
  35. fOnSyncStatus: THookSocketStatus;
  36. procedure DoOnStatus(Sender: TObject; Reason: THookSocketReason; const Value: String);
  37. procedure SyncOnStatus;
  38. public
  39. constructor Create;
  40. destructor Destroy; override;
  41. {:Owner (@link(TCustomConnection))}
  42. property Connection: TCustomConnection read fConnection;
  43. {:Socket status event (synchronized to main thread)}
  44. property OnSyncStatus: THookSocketStatus read fOnSyncStatus write fOnSyncStatus;
  45. end;
  46. {:abstract(Basic connection thread)
  47. This object is used from server and client as working thread.
  48. When object is server connection: object is created automatically by @link(Parent) server.
  49. Thread can be terminated from outside. If server is terminated, all remaining
  50. connections are closed. This object is used to communicate with client.
  51. Object should not be created directly.
  52. }
  53. TCustomConnection = class(TBThread)
  54. private
  55. protected
  56. fIndex: integer;
  57. fParent: TCustomServer;
  58. fSocket: TTCPCustomConnectionSocket;
  59. fSSL: boolean;
  60. procedure AfterConnectionExecute; virtual;
  61. function BeforeExecuteConnection: boolean; virtual;
  62. procedure ExecuteConnection; virtual;
  63. function GetIsTerminated: boolean;
  64. public
  65. constructor Create(aSocket: TTCPCustomConnectionSocket); virtual;
  66. destructor Destroy; override;
  67. {:Thread execute method}
  68. procedure Execute; override;
  69. {:Thread resume method}
  70. procedure Start;
  71. {:Thread suspend method}
  72. procedure Stop;
  73. {:Temination procedure
  74. One should call this procedure to terminate thread,
  75. it internally calls Terminate, but can be overloaded,
  76. and can be used for clean um
  77. }
  78. procedure TerminateThread; virtual;
  79. {:@Connection index.
  80. Automatically generated.
  81. }
  82. property Index: integer read fIndex;
  83. {:@True if thread is not terminated and @link(Socket) exists}
  84. property IsTerminated: boolean read GetIsTerminated;
  85. {:@Connection parent
  86. If client connection, this property is always nil, if server
  87. connection, this property is @link(TCustomServer) that created this connection
  88. }
  89. property Parent: TCustomServer read fParent;
  90. {:@Connection socket}
  91. property Socket: TTCPCustomConnectionSocket read fSocket;
  92. {:Whether SSL is used}
  93. property SSL: boolean read fSSL write fSSL;
  94. end;
  95. { TCustomServerConnection
  96. TCustomServerConnection = class(TCustomConnection)
  97. protected
  98. fBroadcastData: TStringList;
  99. fBroadcastLock: TCriticalSection;
  100. fParent: TCustomServer;
  101. //procedure ExecuteConnection; override;
  102. procedure SyncConnectionRemove;
  103. public
  104. constructor Create(aSocket: TTCPCustomServerConnectionSocket; aParent: TCustomServer); reintroduce; virtual;
  105. destructor Destroy; override;
  106. procedure Execute; override;
  107. :Data setup by server's Broadcast method.
  108. Connection is responsible to send data the data itself.
  109. Connection must delete the data after sending.
  110. procedure Broadcast(aData: string); virtual;
  111. end;
  112. }
  113. {:abstract(Class of connections)}
  114. // TCustomServerConnections = class of TCustomConnection;
  115. {:Event procedural type to hook OnAfterAddConnection in server
  116. Use this hook to get informations about connection accepted server that was added
  117. }
  118. TServerAfterAddConnection = procedure (Server: TCustomServer; aConnection: TCustomConnection) of object;
  119. {:Event procedural type to hook OnBeforeAddConnection in server
  120. Use this hook to be informed that connection is about to be accepred by server.
  121. Use CanAdd parameter (@false) to refuse connection
  122. }
  123. TServerBeforeAddConnection = procedure (Server: TCustomServer; aConnection: TCustomConnection; var CanAdd: boolean) of object;
  124. {:Event procedural type to hook OnAfterRemoveConnection in server
  125. Use this hook to get informations about connection removed from server (connection is closed)
  126. }
  127. TServerAfterRemoveConnection = procedure (Server: TCustomServer; aConnection: TCustomConnection) of object;
  128. {:Event procedural type to hook OnAfterRemoveConnection in server
  129. Use this hook to get informations about connection removed from server (connection is closed)
  130. }
  131. TServerBeforeRemoveConnection = procedure (Server: TCustomServer; aConnection: TCustomConnection) of object;
  132. {:Event procedural type to hook OnSockedError in server
  133. Use this hook to get informations about error on server binding
  134. }
  135. TServerSocketError = procedure (Server: TCustomServer; Socket: TTCPBlockSocket) of object;
  136. {:abstract(Server listening on address and port and spawning @link(TCustomConnection))
  137. Use this object to create server. Object is accepting connections and creating new
  138. server connection objects (@link(TCustomConnection))
  139. }
  140. TCustomServer = class(TBThread)
  141. private
  142. protected
  143. fBind: string;
  144. fPort: string;
  145. fCanAddConnection: boolean;
  146. fConnections: TList;
  147. fConnectionTermLock: TCriticalSection;
  148. fCurrentAddConnection: TCustomConnection;
  149. fCurrentRemoveConnection: TCustomConnection;
  150. fCurrentSocket: TTCPBlockSocket;
  151. fIndex: integer;
  152. fMaxConnectionsCount: integer;
  153. fOnAfterAddConnection: TServerAfterAddConnection;
  154. fOnAfterRemoveConnection: TServerAfterRemoveConnection;
  155. fOnBeforeAddConnection: TServerBeforeAddConnection;
  156. fOnBeforeRemoveConnection: TServerBeforeRemoveConnection;
  157. fOnSocketErrot: TServerSocketError;
  158. fSSL: boolean;
  159. fSSLCertificateFile: string;
  160. fSSLKeyPassword: string;
  161. fSSLPrivateKeyFile: string;
  162. function AddConnection(var aSocket: TTCPCustomConnectionSocket): TCustomConnection; virtual;
  163. {:Main function to determine what kind of connection will be used
  164. @link(AddConnection) uses this functino to actually create connection thread
  165. }
  166. function CreateServerConnection(aSocket: TTCPCustomConnectionSocket): TCustomConnection; virtual;
  167. procedure DoAfterAddConnection; virtual;
  168. procedure DoBeforeAddConnection;
  169. procedure DoAfterRemoveConnection;
  170. procedure DoBeforeRemoveConnection;
  171. procedure DoSocketError;
  172. function GetConnection(index: integer): TCustomConnection;
  173. function GetConnectionByIndex(index: integer): TCustomConnection;
  174. function GetCount: integer;
  175. procedure OnConnectionTerminate(Sender: TObject);
  176. procedure RemoveConnection(aConnection: TCustomConnection);
  177. procedure SyncAfterAddConnection;
  178. procedure SyncBeforeAddConnection;
  179. procedure SyncAfterRemoveConnection;
  180. procedure SyncBeforeRemoveConnection;
  181. procedure SyncSocketError;
  182. public
  183. {:Create new server
  184. aBind represents local IP address server will be listening on.
  185. IP address may be numeric or symbolic ('192.168.74.50', 'cosi.nekde.cz', 'ff08::1').
  186. You can use for listening 0.0.0.0 for localhost
  187. The same for aPort it may be number or mnemonic port ('23', 'telnet').
  188. If port value is '0', system chooses itself and conects unused port in the
  189. range 1024 to 4096 (this depending by operating system!).
  190. Warning: when you call : Bind('0.0.0.0','0'); then is nothing done! In this
  191. case is used implicit system bind instead.
  192. }
  193. constructor Create(aBind: string; aPort: string); virtual;
  194. destructor Destroy; override;
  195. procedure Execute; override;
  196. {:Temination procedure
  197. This method should be called instead of Terminate to terminate thread,
  198. it internally calls Terminate, but can be overloaded,
  199. and can be used for data clean up
  200. }
  201. procedure TerminateThread; virtual;
  202. { :Method used co send the same data to all server connections.
  203. Method only stores data in connection (append to existing data).
  204. Connection must send and delete the data itself.
  205. }
  206. //procedure Broadcast(aData: string); virtual;
  207. {: Procedure to stop removing connections from connections list in case there
  208. is need to walk through it
  209. }
  210. procedure LockTermination;
  211. {:Thread resume method}
  212. procedure Start;
  213. {:Thread suspend method}
  214. procedure Stop;
  215. {: Procedure to resume removing connections. see LockTermination
  216. }
  217. procedure UnLockTermination;
  218. {:Get connection from connection list
  219. Index represent index within connection list (not Connection.Index property)
  220. }
  221. property Connection[index: integer]: TCustomConnection read GetConnection; default;
  222. {:Get connection by its Index}
  223. property ConnectionByIndex[index: integer]: TCustomConnection read GetConnectionByIndex;
  224. {:Valid connections count}
  225. property Count: integer read GetCount;
  226. {:IP address where server is listening (see aBind in constructor)}
  227. property Host: string read fBind;
  228. {:Server index. Automatically generated. }
  229. property Index: integer read fIndex;
  230. {:Maximum number of accepted connections. -1 (default value) represents unlimited number.
  231. If limit is reached and new client is trying to connection, it's refused
  232. }
  233. property MaxConnectionsCount: integer read fMaxConnectionsCount write fMaxConnectionsCount;
  234. {:Port where server is listening (see aPort in constructor)}
  235. property Port: string read fPort;
  236. {:Whether SSL is used}
  237. property SSL: boolean read fSSL write fSSL;
  238. {:SSL certification file}
  239. property SSLCertificateFile: string read fSSLCertificateFile write fSSLCertificateFile;
  240. {:SSL key file}
  241. property SSLKeyPassword: string read fSSLKeyPassword write fSSLKeyPassword;
  242. {:SSL key file}
  243. property SSLPrivateKeyFile: string read fSSLPrivateKeyFile write fSSLPrivateKeyFile;
  244. {:See @link(TServerAfterAddConnection)}
  245. property OnAfterAddConnection: TServerAfterAddConnection read fOnAfterAddConnection write fOnAfterAddConnection;
  246. {:See @link(TServerBeforeAddConnection)}
  247. property OnBeforeAddConnection: TServerBeforeAddConnection read fOnBeforeAddConnection write fOnBeforeAddConnection;
  248. {:See @link(TServerAfterRemoveConnection)}
  249. property OnAfterRemoveConnection: TServerAfterRemoveConnection read fOnAfterRemoveConnection write fOnAfterRemoveConnection;
  250. {:See @link(TServerBeforeRemoveConnection)}
  251. property OnBeforeRemoveConnection: TServerBeforeRemoveConnection read fOnBeforeRemoveConnection write fOnBeforeRemoveConnection;
  252. {:See @link(TServerSocketError)}
  253. property OnSocketError: TServerSocketError read fOnSocketErrot write fOnSocketErrot;
  254. end;
  255. implementation
  256. uses SynSock {$IFDEF WIN32}, Windows {$ENDIF WIN32};
  257. var fConnectionsIndex: Integer = 0;
  258. function getConnectionIndex: integer;
  259. begin
  260. result := fConnectionsIndex;
  261. inc(fConnectionsIndex);
  262. end;
  263. { TCustomServerConnection
  264. procedure TCustomServerConnection.SyncConnectionRemove;
  265. begin
  266. fParent.OnConnectionTerminate(self);
  267. end;
  268. constructor TCustomServerConnection.Create(aSocket: TTCPCustomServerConnectionSocket; aParent: TCustomServer);
  269. begin
  270. fParent := aParent;
  271. fIndex := 0;
  272. fBroadcastLock := TCriticalSection.Create;
  273. fBroadcastData := TStringList.Create;
  274. inherited Create(aSocket);
  275. end;
  276. destructor TCustomServerConnection.Destroy;
  277. begin
  278. fBroadcastData.Free;
  279. fBroadcastLock.free;
  280. inherited Destroy;
  281. end;
  282. procedure TCustomServerConnection.Execute;
  283. begin
  284. try
  285. inherited Execute;
  286. if (not fParent.Terminated) then
  287. Synchronize(SyncConnectionRemove);
  288. //Synchronize(fParent, SyncConnectionRemove);
  289. finally
  290. end;
  291. end;
  292. procedure TCustomServerConnection.Broadcast(aData: string);
  293. begin
  294. if (not IsTerminated) then
  295. begin
  296. fBroadcastLock.Enter;
  297. fBroadcastData.Add(aData);
  298. fBroadcastLock.Leave;
  299. end;
  300. end;
  301. {
  302. procedure TCustomServerConnection.ExecuteConnection;
  303. var s: string;
  304. begin
  305. while(not IsTerminated) do
  306. begin
  307. s := fSocket.RecvString(-1);
  308. if (fSocket <> nil) then
  309. begin
  310. if (fSocket.LastError <> 0) then break;
  311. if (s <> '') then fSocket.SendString(s + #13#10);
  312. if (fSocket.LastError <> 0) then break;
  313. end;
  314. end;
  315. end;
  316. }
  317. { TCustomServer }
  318. procedure TCustomServer.OnConnectionTerminate(Sender: TObject);
  319. begin
  320. try
  321. //OutputDebugString(pChar(Format('srv terminating 1 %d', [TCustomConnection(Sender).Index])));
  322. // fConnectionTermLock.Enter;
  323. //OutputDebugString(pChar(Format('srv terminating 2 %d', [TCustomConnection(Sender).Index])));
  324. RemoveConnection(TCustomConnection(Sender));
  325. //OutputDebugString(pChar(Format('srv terminating 3 %d', [TCustomConnection(Sender).Index])));
  326. // fConnectionTermLock.Leave;
  327. finally
  328. end;
  329. //OutputDebugString(pChar(Format('srv terminating e %d', [TCustomConnection(Sender).Index])));
  330. end;
  331. procedure TCustomServer.RemoveConnection(aConnection: TCustomConnection);
  332. var index: integer;
  333. begin
  334. index := fConnections.IndexOf(aConnection);
  335. if (index <> -1) then
  336. begin
  337. fCurrentRemoveConnection := aConnection;
  338. DoBeforeRemoveConnection;
  339. fConnectionTermLock.Enter;
  340. //OutputDebugString(pChar(Format('removing %d %d %d', [aConnection.fIndex, index, fConnections.Count])));
  341. fConnections.Extract(aConnection);
  342. //fConnections.Delete(index);
  343. //OutputDebugString(pChar(Format('removed %d %d %d', [aConnection.fIndex, index, fConnections.Count])));
  344. fConnectionTermLock.Leave;
  345. DoAfterRemoveConnection;
  346. end;
  347. end;
  348. procedure TCustomServer.DoAfterAddConnection;
  349. begin
  350. if (assigned(fOnAfterAddConnection)) then
  351. Synchronize(SyncAfterAddConnection);
  352. end;
  353. procedure TCustomServer.DoBeforeAddConnection;
  354. begin
  355. if (assigned(fOnBeforeAddConnection)) then
  356. Synchronize(SyncBeforeAddConnection);
  357. end;
  358. procedure TCustomServer.DoAfterRemoveConnection;
  359. begin
  360. if (assigned(fOnAfterRemoveConnection)) then
  361. Synchronize(SyncAfterRemoveConnection);
  362. end;
  363. procedure TCustomServer.DoBeforeRemoveConnection;
  364. begin
  365. if (assigned(fOnBeforeRemoveConnection)) then
  366. Synchronize(SyncBeforeRemoveConnection);
  367. end;
  368. procedure TCustomServer.DoSocketError;
  369. begin
  370. if (assigned(fOnSocketErrot)) then
  371. Synchronize(SyncSocketError);
  372. end;
  373. procedure TCustomServer.SyncAfterAddConnection;
  374. begin
  375. if (assigned(fOnAfterAddConnection)) then
  376. fOnAfterAddConnection(self, fCurrentAddConnection);
  377. end;
  378. procedure TCustomServer.SyncBeforeAddConnection;
  379. begin
  380. if (assigned(fOnBeforeAddConnection)) then
  381. fOnBeforeAddConnection(self, fCurrentAddConnection, fCanAddConnection);
  382. end;
  383. procedure TCustomServer.SyncAfterRemoveConnection;
  384. begin
  385. if (assigned(fOnAfterRemoveConnection)) then
  386. fOnAfterRemoveConnection(self, fCurrentRemoveConnection);
  387. end;
  388. procedure TCustomServer.SyncBeforeRemoveConnection;
  389. begin
  390. if (assigned(fOnBeforeRemoveConnection)) then
  391. fOnBeforeRemoveConnection(self, fCurrentRemoveConnection);
  392. end;
  393. procedure TCustomServer.SyncSocketError;
  394. begin
  395. if (assigned(fOnSocketErrot)) then
  396. fOnSocketErrot(self, fCurrentSocket);
  397. end;
  398. procedure TCustomServer.TerminateThread;
  399. begin
  400. if (terminated) then exit;
  401. Terminate;
  402. end;
  403. constructor TCustomServer.Create(aBind: string; aPort: string);
  404. begin
  405. fBind := aBind;
  406. fPort := aPort;
  407. FreeOnTerminate := true;
  408. fConnections := TList.Create;
  409. fConnectionTermLock := TCriticalSection.Create;
  410. fMaxConnectionsCount := -1;
  411. fCanAddConnection := true;
  412. fCurrentAddConnection := nil;
  413. fCurrentRemoveConnection := nil;
  414. fCurrentSocket := nil;
  415. fIndex := getConnectionIndex;
  416. inherited Create(true);
  417. end;
  418. destructor TCustomServer.Destroy;
  419. begin
  420. fConnectionTermLock.free;
  421. fConnections.free;
  422. inherited Destroy;
  423. end;
  424. function TCustomServer.GetCount: integer;
  425. begin
  426. result := fConnections.Count;
  427. end;
  428. {
  429. procedure TCustomServer.Broadcast(aData: string);
  430. var i: integer;
  431. begin
  432. fConnectionTermLock.Enter;
  433. for i := 0 to fConnections.Count - 1 do
  434. begin
  435. if (not TCustomConnection(fConnections[i]).IsTerminated) then
  436. TCustomServerConnection(fConnections[i]).Broadcast(aData);
  437. end;
  438. fConnectionTermLock.Leave;
  439. end;
  440. }
  441. function TCustomServer.GetConnection(index: integer): TCustomConnection;
  442. begin
  443. fConnectionTermLock.Enter;
  444. result := TCustomConnection(fConnections[index]);
  445. fConnectionTermLock.Leave;
  446. end;
  447. function TCustomServer.GetConnectionByIndex(index: integer): TCustomConnection;
  448. var i: integer;
  449. begin
  450. result := nil;
  451. fConnectionTermLock.Enter;
  452. for i := 0 to fConnections.Count - 1 do
  453. begin
  454. if (TCustomConnection(fConnections[i]).Index = index) then
  455. begin
  456. result := TCustomConnection(fConnections[i]);
  457. break;
  458. end;
  459. end;
  460. fConnectionTermLock.Leave;
  461. end;
  462. function TCustomServer.CreateServerConnection(aSocket: TTCPCustomConnectionSocket): TCustomConnection;
  463. begin
  464. result := nil;
  465. end;
  466. function TCustomServer.AddConnection(var aSocket: TTCPCustomConnectionSocket): TCustomConnection;
  467. begin
  468. if ((fMaxConnectionsCount = -1) or (fConnections.count < fMaxConnectionsCount)) then
  469. begin
  470. result := CreateServerConnection(aSocket);
  471. if (result <> nil) then
  472. begin
  473. result.fParent := self;
  474. fCurrentAddConnection := result;
  475. fCanAddConnection := true;
  476. DoBeforeAddConnection;
  477. if (fCanAddConnection) then
  478. begin
  479. fConnections.add(result);
  480. DoAfterAddConnection;
  481. result.Resume;
  482. end
  483. else
  484. begin
  485. FreeAndNil(result);
  486. //aSocket := nil;
  487. end;
  488. end
  489. //else aSocket := nil;
  490. end;
  491. end;
  492. procedure TCustomServer.Execute;
  493. var
  494. c: TCustomConnection;
  495. s: TTCPCustomConnectionSocket;
  496. sock: TSocket;
  497. i: integer;
  498. begin
  499. fCurrentSocket := TTCPBlockSocket.Create;
  500. with fCurrentSocket do
  501. begin
  502. CreateSocket;
  503. if lastError <> 0 then DoSocketError;
  504. SetLinger(true, 10000);
  505. if lastError <> 0 then DoSocketError;
  506. bind(fBind, fPort);
  507. if lastError <> 0 then DoSocketError;
  508. listen;
  509. if lastError <> 0 then DoSocketError;
  510. repeat
  511. if terminated then
  512. break;
  513. if canread(1000) then
  514. begin
  515. if LastError = 0 then
  516. begin
  517. sock := Accept;
  518. if lastError = 0 then
  519. begin
  520. s := TTCPCustomConnectionSocket.Create;
  521. s.Socket := sock;
  522. if (fSSL) then
  523. begin
  524. s.SSL.CertificateFile := fSSLCertificateFile;
  525. s.SSL.PrivateKeyFile := fSSLPrivateKeyFile;
  526. //s.SSL.SSLType := LT_SSLv3;
  527. if (SSLKeyPassword <> '') then
  528. s.SSL.KeyPassword := fSSLKeyPassword;
  529. s.SSLAcceptConnection;
  530. i := s.SSL.LastError;
  531. if (i <> 0) then
  532. begin
  533. FreeAndNil(s);
  534. end;
  535. end;
  536. if (s <> nil) then
  537. begin
  538. s.GetSins;
  539. c := AddConnection(s);
  540. if (c = nil) and (s <> nil) then
  541. s.Free;
  542. end;
  543. end
  544. else
  545. begin
  546. DoSocketError;
  547. end;
  548. end
  549. else
  550. begin
  551. if lastError <> WSAETIMEDOUT then
  552. DoSocketError;
  553. end;
  554. end;
  555. until false;
  556. end;
  557. fOnAfterAddConnection := nil;
  558. fOnBeforeAddConnection := nil;
  559. fOnAfterRemoveConnection := nil;
  560. fOnBeforeRemoveConnection := nil;
  561. fOnSocketErrot := nil;
  562. //while fConnections.Count > 0 do
  563. for i := fConnections.Count - 1 downto 0 do
  564. begin
  565. c := TCustomConnection(fConnections[i]);
  566. try
  567. OnConnectionTerminate(c);
  568. c.TerminateThread;
  569. {$IFDEF WIN32} WaitForSingleObject(c.Handle, 100) {$ELSE WIN32} sleep(100); {$ENDIF WIN32}
  570. finally end;
  571. end;
  572. FreeAndNil(fCurrentSocket);
  573. //while fConnections.Count > 0 do sleep(500);
  574. end;
  575. procedure TCustomServer.LockTermination;
  576. begin
  577. fConnectionTermLock.Enter;
  578. end;
  579. procedure TCustomServer.Start;
  580. begin
  581. Resume;
  582. end;
  583. procedure TCustomServer.Stop;
  584. begin
  585. Suspend;
  586. end;
  587. procedure TCustomServer.UnLockTermination;
  588. begin
  589. fConnectionTermLock.Leave;
  590. end;
  591. { TTCPCustomServerConnectionSocket }
  592. { TTCPCustomConnectionSocket }
  593. destructor TTCPCustomConnectionSocket.Destroy;
  594. begin
  595. OnStatus := nil;
  596. OnSyncStatus := nil;
  597. inherited;
  598. end;
  599. procedure TTCPCustomConnectionSocket.DoOnStatus(Sender: TObject; Reason: THookSocketReason; const Value: String);
  600. begin
  601. if (fConnection <> nil) and (not fConnection.terminated) and (assigned(fOnSyncStatus)) then
  602. begin
  603. fCurrentStatusReason := Reason;
  604. fCurrentStatusValue := value;
  605. fConnection.Synchronize(SyncOnStatus);
  606. {
  607. if (fCurrentStatusReason = HR_Error) and (LastError = WSAECONNRESET) then
  608. fConnection.Terminate;
  609. }
  610. end;
  611. end;
  612. procedure TTCPCustomConnectionSocket.SyncOnStatus;
  613. begin
  614. if (assigned(fOnSyncStatus)) then
  615. fOnSyncStatus(self, fCurrentStatusReason, fCurrentStatusValue);
  616. end;
  617. constructor TTCPCustomConnectionSocket.Create;
  618. begin
  619. inherited Create;
  620. fConnection := nil;
  621. OnStatus := DoOnStatus;
  622. end;
  623. { TCustomConnection }
  624. constructor TCustomConnection.Create(aSocket: TTCPCustomConnectionSocket);
  625. begin
  626. fSocket := aSocket;
  627. fSocket.fConnection := self;
  628. FreeOnTerminate := true;
  629. fIndex := getConnectionIndex;
  630. inherited Create(true);
  631. end;
  632. destructor TCustomConnection.Destroy;
  633. begin
  634. if (fSocket <> nil) then
  635. begin
  636. fSocket.OnSyncStatus := nil;
  637. fSocket.OnStatus := nil;
  638. fSocket.Free;
  639. end;
  640. inherited Destroy;
  641. end;
  642. procedure TCustomConnection.Execute;
  643. begin
  644. if (BeforeExecuteConnection) then
  645. begin
  646. ExecuteConnection;
  647. AfterConnectionExecute;
  648. end;
  649. if (fParent <> nil) then
  650. if (not fParent.Terminated) then
  651. fParent.OnConnectionTerminate(self);
  652. end;
  653. procedure TCustomConnection.Start;
  654. begin
  655. Resume;
  656. end;
  657. procedure TCustomConnection.Stop;
  658. begin
  659. Suspend;
  660. end;
  661. procedure TCustomConnection.TerminateThread;
  662. begin
  663. if (terminated) then exit;
  664. Socket.OnSyncStatus := nil;
  665. Socket.OnStatus := nil;
  666. Terminate;
  667. end;
  668. function TCustomConnection.GetIsTerminated: boolean;
  669. begin
  670. result := terminated or (fSocket = nil)// or (fSocket.Socket = INVALID_SOCKET);
  671. end;
  672. procedure TCustomConnection.AfterConnectionExecute;
  673. begin
  674. end;
  675. function TCustomConnection.BeforeExecuteConnection: boolean;
  676. begin
  677. result := true;
  678. end;
  679. procedure TCustomConnection.ExecuteConnection;
  680. begin
  681. end;
  682. end.