SynSrv.pas 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. {--------------------------------------------------------------}
  2. { }
  3. { SynSrv.pas - generic TCP server over Synapse library }
  4. { }
  5. { Author: Semi }
  6. { Started: 070528 }
  7. { }
  8. {--------------------------------------------------------------}
  9. unit SynSrv;
  10. {$IFDEF FPC}
  11. {$MODE Delphi}
  12. {$ENDIF}
  13. interface
  14. uses
  15. SysUtils,
  16. Classes,
  17. synsock,
  18. blcksock,
  19. Generics.Collections;
  20. //-------------------------------------------------------------
  21. const
  22. // Default timeout to receive 1 line from connection:
  23. cDefLineTimeout = 120000; // default 2 minutes...
  24. type
  25. TSynTcpSrvConnection = class;
  26. TSynTcpServer = class;
  27. { TListenerThread }
  28. TListenerThread = class(TThread)
  29. private
  30. FThreadList: TThreadList<TSynTcpSrvConnection>;
  31. FSocket: TTCPBlockSocket;
  32. FPort: string;
  33. FHost: string;
  34. FTcpServer: TSynTcpServer;
  35. procedure ClearFinishedThreads;
  36. procedure BindSocket;
  37. protected
  38. procedure Execute; override;
  39. public
  40. constructor Create(ASuspended: boolean; ATcpServer: TSynTcpServer);
  41. destructor Destroy; override;
  42. property Host: string Read FHost Write FHost;
  43. property Port: string Read FPort Write FPort;
  44. property Socket: TTCPBlockSocket Read FSocket;
  45. end;
  46. TSynTcpSrvConnection = class(TThread)
  47. private
  48. FTcpServer: TSynTcpServer;
  49. FFinished: boolean;
  50. FSocket: TTCPBlockSocket;
  51. function GetClientAddress: string;
  52. function GetClientPort: integer;
  53. protected
  54. procedure Execute; override;
  55. public
  56. destructor Destroy; override;
  57. constructor Create(ASuspended: boolean; ASocket: TSocket; ATcpServer: TSynTcpServer);
  58. property Socket: TTCPBlockSocket Read FSocket Write FSocket; // client socket
  59. property ClientAddress: string Read GetClientAddress; // '123.45.67.89'
  60. property ClientPort: integer Read GetClientPort;
  61. end;
  62. TCommandHandler = procedure(Connection: TSynTcpSrvConnection; Command: string) of object;
  63. // TSynTcpServer - Generic TCP server component
  64. [ComponentPlatformsAttribute(pidWin32 or pidWin64)]
  65. TSynTcpServer = class(TComponent)
  66. protected
  67. FActive: boolean;
  68. FPort: string;
  69. FHost: string;
  70. FHTTPSEnabled: boolean;
  71. //
  72. FOnCommand: TCommandHandler;
  73. //
  74. FSynapseServer: TListenerThread;
  75. procedure SetPort(const Value: string);
  76. procedure SetLocalAddr(const Value: string);
  77. procedure SetActive(Value: boolean); virtual;
  78. public
  79. constructor Create(AOwner: TComponent); override;
  80. //
  81. //
  82. published
  83. // Host may be assigned to 'localhost' to serve only on localhost interface...
  84. property Host: string Read FHost Write FHost;
  85. //
  86. // Port must be assigned.
  87. property Port: string Read FPort Write SetPort; // MUST assign port...
  88. //
  89. // Set Active:=True to start server, set Active:=False to stop server
  90. property Active: boolean Read FActive Write SetActive default False;
  91. //
  92. // Or assign OnCommand to parse commands (text lines) from connection:
  93. // (this is used by TSynHttpServer and TSynFtpServer etc...)
  94. property OnCommand: TCommandHandler Read FOnCommand Write FOnCommand;
  95. property HTTPSEnabled: boolean Read FHTTPSEnabled Write FHTTPSEnabled;
  96. end;
  97. //-------------------------------------------------------------
  98. implementation
  99. //-------------------------------------------------------------
  100. { TSynTcpServer }
  101. constructor TSynTcpServer.Create(AOwner: TComponent);
  102. begin
  103. inherited;
  104. //
  105. FHost := '0.0.0.0';
  106. end;
  107. procedure TSynTcpServer.SetPort(const Value: string);
  108. begin
  109. SetActive(False);
  110. FPort := Value;
  111. end;
  112. procedure TSynTcpServer.SetLocalAddr(const Value: string);
  113. begin
  114. SetActive(False);
  115. FHost := Value;
  116. end;
  117. procedure TSynTcpServer.SetActive(Value: boolean);
  118. begin
  119. if (csDesigning in ComponentState) then
  120. begin
  121. // No real server at design-time...
  122. FActive := Value;
  123. Exit;
  124. end;
  125. if (csLoading in ComponentState) then
  126. Exit;
  127. //
  128. if (FActive <> Value) then
  129. begin
  130. FActive := Value;
  131. if FActive then
  132. begin
  133. if (FPort = '') then
  134. raise ESynapseError.Create('Missing server Port');
  135. FSynapseServer := TListenerThread.Create(True, Self);
  136. FSynapseServer.Port := FPort;
  137. FSynapseServer.Host := FHost;
  138. try
  139. FSynapseServer.BindSocket
  140. except
  141. FreeAndNil(FSynapseServer);
  142. FActive := False;
  143. raise ESocketBindError.Create(Format('Couldnt bind socket on %s port', [FPort]));
  144. end;
  145. FSynapseServer.Start;
  146. end
  147. else
  148. if Assigned(FSynapseServer) then
  149. begin
  150. FSynapseServer.Terminate;
  151. FSynapseServer.WaitFor;
  152. FreeAndNil(FSynapseServer);
  153. //StopAllSessions;
  154. end;
  155. end;
  156. end;
  157. { TListenerThread }
  158. procedure TListenerThread.ClearFinishedThreads;
  159. var
  160. i: integer;
  161. List: TList<TSynTcpSrvConnection>;
  162. begin
  163. List := FThreadList.LockList;
  164. try
  165. for i := List.Count - 1 downto 0 do
  166. if List[i].FFinished then
  167. begin
  168. List[i].Free;
  169. List.Remove(List[i]);
  170. end;
  171. finally
  172. FThreadList.UnlockList;
  173. end;
  174. end;
  175. procedure TListenerThread.BindSocket;
  176. var
  177. e: ESynapseError;
  178. begin
  179. FSocket.CreateSocket;
  180. FSocket.Bind(FHost, FPort);
  181. if FSocket.LastError = 0 then
  182. begin
  183. FSocket.EnableReuse(True);
  184. FSocket.Listen;
  185. end
  186. else
  187. begin
  188. e := ESynapseError.Create(Format('ListenThreadException %d: %s', [FSocket.LastError, FSocket.LastErrorDesc]));
  189. e.ErrorCode := FSocket.LastError;
  190. e.ErrorMessage := FSocket.LastErrorDesc;
  191. raise e;
  192. end;
  193. end;
  194. constructor TListenerThread.Create(ASuspended: boolean; ATcpServer: TSynTcpServer);
  195. begin
  196. FSocket := TTCPBlockSocket.Create;
  197. FThreadList := TThreadList<TSynTcpSrvConnection>.Create;
  198. FTcpServer := ATcpServer;
  199. inherited Create(ASuspended);
  200. end;
  201. destructor TListenerThread.Destroy;
  202. var
  203. i: integer;
  204. List: TList<TSynTcpSrvConnection>;
  205. begin
  206. FSocket.CloseSocket;
  207. List := FThreadList.LockList;
  208. try
  209. for i := 0 to List.Count - 1 do
  210. begin
  211. List[i].Terminate;
  212. List[i].Socket.CloseSocket;
  213. List[i].Free;
  214. end;
  215. finally
  216. FThreadList.UnlockList;
  217. end;
  218. FreeAndNil(FThreadList);
  219. FreeAndNil(FSocket);
  220. inherited;
  221. end;
  222. procedure TListenerThread.Execute;
  223. var
  224. SynapseConnect: TSynTcpSrvConnection;
  225. begin
  226. repeat
  227. if FSocket.CanRead(100) then
  228. begin
  229. SynapseConnect := TSynTcpSrvConnection.Create(True, FSocket.Accept, FTcpServer);
  230. FThreadList.Add(SynapseConnect);
  231. SynapseConnect.Start;
  232. end;
  233. ClearFinishedThreads;
  234. until Terminated;
  235. try
  236. FSocket.CloseSocket;
  237. except
  238. end;
  239. end;
  240. { TSynTcpSrvConnection }
  241. constructor TSynTcpSrvConnection.Create(ASuspended: boolean; ASocket: TSocket; ATcpServer: TSynTcpServer);
  242. begin
  243. inherited Create(ASuspended);
  244. FSocket := TTCPBlockSocket.Create;
  245. FSocket.Owner := Self;
  246. FSocket.SSL.CertificateFile := ATcpServer.FSynapseServer.FSocket.SSL.CertificateFile;
  247. FSocket.SSL.PrivateKeyFile := ATcpServer.FSynapseServer.FSocket.SSL.PrivateKeyFile;
  248. FSocket.SSL.KeyPassword := ATcpServer.FSynapseServer.FSocket.SSL.KeyPassword;
  249. FSocket.SSL.VerifyCert := ATcpServer.FSynapseServer.FSocket.SSL.VerifyCert;
  250. FTcpServer := ATcpServer;
  251. if ASocket <> INVALID_SOCKET then
  252. begin
  253. FSocket.Socket := ASocket;
  254. FSocket.GetSins;
  255. end;
  256. end;
  257. destructor TSynTcpSrvConnection.Destroy;
  258. begin
  259. FSocket.CloseSocket;
  260. inherited;
  261. FreeAndNil(FSocket);
  262. end;
  263. procedure TSynTcpSrvConnection.Execute;
  264. var
  265. Command: string;
  266. begin
  267. inherited;
  268. if FSocket.SSL.VerifyCert then
  269. try
  270. if (not FSocket.SSLAcceptConnection) or (FSocket.SSL.LastError <> 0) then
  271. begin
  272. FFinished := True;
  273. end;
  274. except
  275. FFinished := True;
  276. end;
  277. if not FFinished then
  278. try
  279. while not Terminated do
  280. begin
  281. Command := string(FSocket.RecvString({FSocket.GetRecvTimeout)}cDefLineTimeout));
  282. // Disconnect on timeout:
  283. if (Command = '') and (FSocket.LastError <> 0) then
  284. Break;
  285. //
  286. if Assigned(FTcpServer.FOnCommand) then // could be de-assigned?
  287. FTcpServer.FOnCommand(Self, Command)
  288. else
  289. Break;
  290. end;
  291. finally
  292. FFinished := True;
  293. end;
  294. end;
  295. function TSynTcpSrvConnection.GetClientAddress: string;
  296. begin
  297. Result := FSocket.GetRemoteSinIP;
  298. end;
  299. function TSynTcpSrvConnection.GetClientPort: integer;
  300. begin
  301. Result := FSocket.GetRemoteSinPort;
  302. end;
  303. end.