fpsock.pp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579
  1. {
  2. Socket communication components
  3. Copyright (c) 2003 by
  4. Areca Systems GmbH / Sebastian Guenther, [email protected]
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. }
  11. unit fpSock;
  12. interface
  13. uses Errors, SysUtils, Sockets, Classes, fpAsync, Resolve;
  14. type
  15. ESocketError = class(Exception)
  16. end;
  17. TSocketComponent = class(TComponent)
  18. private
  19. FEventLoop: TEventLoop;
  20. public
  21. property EventLoop: TEventLoop read FEventLoop write FEventLoop;
  22. end;
  23. TSocketStream = class(THandleStream)
  24. private
  25. FOnDisconnect: TNotifyEvent;
  26. function GetLocalAddress: TSockAddr;
  27. function GetPeerAddress: TSockAddr;
  28. protected
  29. procedure Disconnected; virtual;
  30. public
  31. destructor Destroy; override;
  32. function Read(var Buffer; Count: LongInt): LongInt; override;
  33. function Write(const Buffer; Count: LongInt): LongInt; override;
  34. property LocalAddress: TSockAddr read GetLocalAddress;
  35. property PeerAddress: TSockAddr read GetPeerAddress;
  36. property OnDisconnect: TNotifyEvent read FOnDisconnect write FOnDisconnect;
  37. end;
  38. // Connection-based sockets
  39. TConnectionBasedSocket = class(TSocketComponent)
  40. protected
  41. FStream: TSocketStream;
  42. FActive: Boolean;
  43. procedure SetActive(Value: Boolean); virtual; abstract;
  44. property Active: Boolean read FActive write SetActive;
  45. property Stream: TSocketStream read FStream;
  46. public
  47. destructor Destroy; override;
  48. end;
  49. TConnectionState = (
  50. connDisconnected,
  51. connResolving,
  52. connConnecting,
  53. connConnected);
  54. TClientConnectionSocket = class;
  55. TConnectionStateChangeEvent = procedure(Sender: TClientConnectionSocket;
  56. OldState, NewState: TConnectionState) of object;
  57. TClientConnectionSocket = class(TConnectionBasedSocket)
  58. private
  59. FOnStateChange: TConnectionStateChangeEvent;
  60. FRetries: Integer;
  61. FRetryDelay: Integer; // Delay between retries in ms
  62. RetryCounter: Integer;
  63. RetryTimerNotifyHandle: Pointer;
  64. CanWriteNotifyHandle: Pointer;
  65. procedure RetryTimerNotify(Sender: TObject);
  66. procedure SocketCanWrite(Sender: TObject);
  67. protected
  68. FConnectionState: TConnectionState;
  69. procedure CreateSocket; virtual; abstract;
  70. procedure DoResolve; virtual;
  71. procedure DoConnect; virtual;
  72. function GetPeerName: String; virtual; abstract;
  73. procedure SetActive(Value: Boolean); override;
  74. procedure SetConnectionState(NewState: TConnectionState);
  75. property ConnectionState: TConnectionState read FConnectionState;
  76. property Retries: Integer read FRetries write FRetries default 0;
  77. property RetryDelay: Integer read FRetryDelay write FRetryDelay default 500;
  78. property OnConnectionStateChange: TConnectionStateChangeEvent
  79. read FOnStateChange write FOnStateChange;
  80. public
  81. constructor Create(AOwner: TComponent); override;
  82. destructor Destroy; override;
  83. end;
  84. TQueryConnectEvent = procedure(Sender: TConnectionBasedSocket; Socket: Integer;
  85. var DoConnect: Boolean) of object;
  86. TConnectEvent = procedure(Sender: TConnectionBasedSocket;
  87. Stream: TSocketStream) of object;
  88. TSocketConnectionServer = class(TConnectionBasedSocket)
  89. private
  90. FOnQueryConnect: TQueryConnectEvent;
  91. FOnConnect: TConnectEvent;
  92. protected
  93. DataAvailableNotifyHandle: Pointer;
  94. procedure ListenerDataAvailable(Sender: TObject);
  95. function DoQueryConnect(ASocket: Integer): Boolean;
  96. procedure DoConnect(AStream: TSocketStream); virtual;
  97. property OnQueryConnect: TQueryConnectEvent read FOnQueryConnect
  98. write FOnQueryConnect;
  99. property OnConnect: TConnectEvent read FOnConnect write FOnConnect;
  100. end;
  101. // TCP/IP components
  102. TCustomTCPClient = class(TClientConnectionSocket)
  103. private
  104. FHost: String;
  105. FPort: Word;
  106. HostAddr: THostAddr;
  107. procedure SetHost(const Value: String);
  108. procedure SetPort(Value: Word);
  109. protected
  110. procedure CreateSocket; override;
  111. procedure DoResolve; override;
  112. procedure DoConnect; override;
  113. function GetPeerName: String; override;
  114. property Host: String read FHost write SetHost;
  115. property Port: Word read FPort write SetPort;
  116. public
  117. destructor Destroy; override;
  118. end;
  119. TTCPClient = class(TCustomTCPClient)
  120. public
  121. property ConnectionState;
  122. property Stream;
  123. published
  124. property Active;
  125. property Host;
  126. property Port;
  127. property Retries;
  128. property RetryDelay;
  129. property OnConnectionStateChange;
  130. end;
  131. TCustomTCPServer = class;
  132. TCustomTCPServer = class(TSocketConnectionServer)
  133. private
  134. FPort: Word;
  135. procedure SetActive(Value: Boolean); override;
  136. protected
  137. //!!!: Interface/bindings list?
  138. property Port: Word read FPort write FPort;
  139. public
  140. destructor Destroy; override;
  141. end;
  142. TTCPServer = class(TCustomTCPServer)
  143. public
  144. property Stream;
  145. published
  146. property Active;
  147. property Port;
  148. property OnQueryConnect;
  149. property OnConnect;
  150. end;
  151. implementation
  152. uses
  153. baseunix,Unix;
  154. resourcestring
  155. SSocketNoEventLoopAssigned = 'No event loop assigned';
  156. SSocketCreationError = 'Could not create socket: %s';
  157. SHostNotFound = 'Host "%s" not found';
  158. SSocketConnectFailed = 'Could not connect to %s: %s';
  159. SSocketBindingError = 'Could not bind socket to port %d: %s';
  160. SSocketAcceptError = 'Connection accept failed: %s';
  161. SSocketIsActive = 'Cannot change parameters while active';
  162. Const
  163. Sys_EAGAIN = ESYSEAGAIN;
  164. Sys_EINPROGRESS = ESYSEINPROGRESS;
  165. // TSocketStream
  166. destructor TSocketStream.Destroy;
  167. begin
  168. FileClose(Handle);
  169. inherited Destroy;
  170. end;
  171. function TSocketStream.Read(var Buffer; Count: LongInt): LongInt;
  172. begin
  173. Result := recv(Handle, Buffer, Count, MSG_NOSIGNAL);
  174. if Result = -1 then
  175. begin
  176. Result := 0;
  177. if SocketError <> Sys_EAGAIN then
  178. Disconnected;
  179. end;
  180. end;
  181. function TSocketStream.Write(const Buffer; Count: LongInt): LongInt;
  182. begin
  183. Result := send(Handle, Buffer, Count, MSG_NOSIGNAL);
  184. if Result = -1 then
  185. begin
  186. Result := 0;
  187. if SocketError <> Sys_EAGAIN then
  188. Disconnected;
  189. end;
  190. end;
  191. procedure TSocketStream.Disconnected;
  192. begin
  193. if Assigned(OnDisconnect) then
  194. OnDisconnect(Self);
  195. end;
  196. function TSocketStream.GetLocalAddress: TSockAddr;
  197. var
  198. len: LongInt;
  199. begin
  200. len := SizeOf(TSockAddr);
  201. if GetSocketName(Handle, Result, len) <> 0 then
  202. FillChar(Result, SizeOf(Result), 0);
  203. end;
  204. function TSocketStream.GetPeerAddress: TSockAddr;
  205. var
  206. len: LongInt;
  207. begin
  208. len := SizeOf(TSockAddr);
  209. if GetPeerName(Handle, Result, len) <> 0 then
  210. FillChar(Result, SizeOf(Result), 0);
  211. end;
  212. // TConnectionBasedSocket
  213. destructor TConnectionBasedSocket.Destroy;
  214. begin
  215. FreeAndNil(FStream);
  216. inherited Destroy;
  217. end;
  218. // TClientConnectionSocket
  219. constructor TClientConnectionSocket.Create(AOwner: TComponent);
  220. begin
  221. inherited Create(AOwner);
  222. FRetryDelay := 500;
  223. end;
  224. destructor TClientConnectionSocket.Destroy;
  225. begin
  226. if Assigned(RetryTimerNotifyHandle) then
  227. EventLoop.RemoveTimerNotify(RetryTimerNotifyHandle);
  228. inherited Destroy;
  229. end;
  230. procedure TClientConnectionSocket.DoResolve;
  231. begin
  232. // By default, no resolving is done, so continue directly with connecting
  233. DoConnect;
  234. end;
  235. procedure TClientConnectionSocket.DoConnect;
  236. begin
  237. SetConnectionState(connConnecting);
  238. try
  239. if not Assigned(EventLoop) then
  240. raise ESocketError.Create(SSocketNoEventLoopAssigned);
  241. CanWriteNotifyHandle := EventLoop.SetCanWriteNotify(Stream.Handle,
  242. @SocketCanWrite, nil);
  243. except
  244. SetConnectionState(connDisconnected);
  245. raise;
  246. end;
  247. end;
  248. procedure TClientConnectionSocket.SetActive(Value: Boolean);
  249. begin
  250. if Value <> Active then
  251. begin
  252. if Value then
  253. begin
  254. // Activate the connection
  255. FActive := True;
  256. RetryCounter := 0;
  257. CreateSocket;
  258. DoResolve;
  259. end else
  260. begin
  261. // Close the connection
  262. FActive := False;
  263. try
  264. FreeAndNil(FStream);
  265. if Assigned(CanWriteNotifyHandle) then
  266. begin
  267. EventLoop.ClearCanWriteNotify(CanWriteNotifyHandle);
  268. CanWriteNotifyHandle := nil;
  269. end;
  270. if Assigned(RetryTimerNotifyHandle) then
  271. begin
  272. EventLoop.RemoveTimerNotify(RetryTimerNotifyHandle);
  273. RetryTimerNotifyHandle := nil;
  274. end;
  275. finally
  276. SetConnectionState(connDisconnected);
  277. end;
  278. end;
  279. end;
  280. end;
  281. procedure TClientConnectionSocket.SetConnectionState(NewState:
  282. TConnectionState);
  283. var
  284. OldState: TConnectionState;
  285. begin
  286. if NewState <> ConnectionState then
  287. begin
  288. OldState := ConnectionState;
  289. FConnectionState := NewState;
  290. if Assigned(OnConnectionStateChange) then
  291. OnConnectionStateChange(Self, OldState, NewState);
  292. end;
  293. end;
  294. procedure TClientConnectionSocket.RetryTimerNotify(Sender: TObject);
  295. begin
  296. RetryTimerNotifyHandle := nil;
  297. Active := True;
  298. end;
  299. procedure TClientConnectionSocket.SocketCanWrite(Sender: TObject);
  300. var
  301. Error: Integer;
  302. ErrorLen, GetResult: LongInt;
  303. begin
  304. if ConnectionState = connConnecting then
  305. begin
  306. EventLoop.ClearCanWriteNotify(CanWriteNotifyHandle);
  307. CanWriteNotifyHandle := nil;
  308. ErrorLen := SizeOf(Error);
  309. GetResult := Sockets.GetSocketOptions(Stream.Handle, SOL_SOCKET, SO_ERROR,
  310. Error, ErrorLen);
  311. if GetResult <> 0 then
  312. raise ESocketError.CreateFmt(SSocketConnectFailed,
  313. [GetPeerName, StrError(GetResult)]);
  314. if Error <> 0 then
  315. if (RetryCounter >= Retries) and (Retries >= 0) then
  316. raise ESocketError.CreateFmt(SSocketConnectFailed,
  317. [GetPeerName, StrError(Error)])
  318. else begin
  319. Active := False;
  320. RetryTimerNotifyHandle := EventLoop.AddTimerNotify(RetryDelay, False,
  321. @RetryTimerNotify, Self);
  322. Inc(RetryCounter);
  323. end
  324. else
  325. begin
  326. RetryCounter := 0;
  327. SetConnectionState(connConnected);
  328. end;
  329. end;
  330. end;
  331. // TSocketConnectionServer
  332. procedure TSocketConnectionServer.ListenerDataAvailable(Sender: TObject);
  333. var
  334. ClientSocket: Integer;
  335. Addr: TInetSockAddr;
  336. AddrSize: Integer;
  337. begin
  338. AddrSize := SizeOf(Addr);
  339. ClientSocket := Accept(Stream.Handle, Addr, AddrSize);
  340. if ClientSocket = -1 then
  341. raise ESocketError.CreateFmt(SSocketAcceptError, [StrError(SocketError)]);
  342. if DoQueryConnect(ClientSocket) then
  343. DoConnect(TSocketStream.Create(ClientSocket));
  344. end;
  345. function TSocketConnectionServer.DoQueryConnect(ASocket: Integer): Boolean;
  346. begin
  347. Result := True;
  348. if Assigned(OnQueryConnect) then
  349. OnQueryConnect(Self, ASocket, Result);
  350. end;
  351. procedure TSocketConnectionServer.DoConnect(AStream: TSocketStream);
  352. begin
  353. if Assigned(OnConnect) then
  354. OnConnect(Self, AStream);
  355. end;
  356. // TCustomTCPClient
  357. type
  358. TClientSocketStream = class(TSocketStream)
  359. protected
  360. Client: TCustomTCPClient;
  361. procedure Disconnected; override;
  362. end;
  363. procedure TClientSocketStream.Disconnected;
  364. begin
  365. inherited Disconnected;
  366. Client.Active := False;
  367. end;
  368. destructor TCustomTCPClient.Destroy;
  369. begin
  370. if Assigned(CanWriteNotifyHandle) then
  371. begin
  372. EventLoop.ClearCanWriteNotify(CanWriteNotifyHandle);
  373. // Set to nil to be sure that descendant classes don't do something stupid
  374. CanWriteNotifyHandle := nil;
  375. end;
  376. inherited Destroy;
  377. end;
  378. procedure TCustomTCPClient.SetHost(const Value: String);
  379. begin
  380. if Value <> Host then
  381. begin
  382. if Active then
  383. raise ESocketError.Create(SSocketIsActive);
  384. FHost := Value;
  385. end;
  386. end;
  387. procedure TCustomTCPClient.SetPort(Value: Word);
  388. begin
  389. if Value <> Port then
  390. begin
  391. if Active then
  392. raise ESocketError.Create(SSocketIsActive);
  393. FPort := Value;
  394. end;
  395. end;
  396. procedure TCustomTCPClient.DoResolve;
  397. var
  398. HostResolver: THostResolver;
  399. begin
  400. HostAddr := StrToNetAddr(Host);
  401. if HostAddr.s_bytes[4] = 0 then
  402. begin
  403. HostResolver := THostResolver.Create(nil);
  404. try
  405. SetConnectionState(connResolving);
  406. if not HostResolver.NameLookup(FHost) then
  407. raise ESocketError.CreateFmt(SHostNotFound, [Host]);
  408. HostAddr := HostResolver.HostAddress;
  409. finally
  410. HostResolver.Free;
  411. end;
  412. end;
  413. DoConnect;
  414. end;
  415. procedure TCustomTCPClient.CreateSocket;
  416. var
  417. Socket: Integer;
  418. begin
  419. Socket := Sockets.Socket(AF_INET, SOCK_STREAM, 0);
  420. if Socket = -1 then
  421. raise ESocketError.CreateFmt(SSocketCreationError,
  422. [StrError(SocketError)]);
  423. FStream := TClientSocketStream.Create(Socket);
  424. TClientSocketStream(FStream).Client := Self;
  425. end;
  426. procedure TCustomTCPClient.DoConnect;
  427. var
  428. SockAddr: TInetSockAddr;
  429. begin
  430. inherited DoConnect;
  431. SockAddr.Family := AF_INET;
  432. SockAddr.Port := ShortHostToNet(Port);
  433. SockAddr.Addr := Cardinal(HostAddr);
  434. Sockets.Connect(Stream.Handle, SockAddr, SizeOf(SockAddr));
  435. if (SocketError <> sys_EINPROGRESS) and (SocketError <> 0) then
  436. raise ESocketError.CreateFmt(SSocketConnectFailed,
  437. [GetPeerName, StrError(SocketError)]);
  438. end;
  439. function TCustomTCPClient.GetPeerName: String;
  440. begin
  441. Result := Format('%s:%d', [Host, Port]);
  442. end;
  443. // TCustomTCPServer
  444. destructor TCustomTCPServer.Destroy;
  445. begin
  446. if Assigned(DataAvailableNotifyHandle) then
  447. begin
  448. EventLoop.ClearDataAvailableNotify(DataAvailableNotifyHandle);
  449. // Set to nil to be sure that descendant classes don't do something stupid
  450. DataAvailableNotifyHandle := nil;
  451. end;
  452. inherited Destroy;
  453. end;
  454. procedure TCustomTCPServer.SetActive(Value: Boolean);
  455. var
  456. Socket, TrueValue: Integer;
  457. Addr: TInetSockAddr;
  458. begin
  459. if Active <> Value then
  460. begin
  461. FActive := False;
  462. if Value then
  463. begin
  464. Socket := Sockets.Socket(AF_INET, SOCK_STREAM, 0);
  465. if Socket = -1 then
  466. raise ESocketError.CreateFmt(SSocketCreationError,
  467. [StrError(SocketError)]);
  468. TrueValue := 1;
  469. Sockets.SetSocketOptions(Socket, SOL_SOCKET, SO_REUSEADDR,
  470. TrueValue, SizeOf(TrueValue));
  471. FStream := TSocketStream.Create(Socket);
  472. Addr.Family := AF_INET;
  473. Addr.Port := ShortHostToNet(Port);
  474. Addr.Addr := 0;
  475. if not Bind(Socket, Addr, SizeOf(Addr)) then
  476. raise ESocketError.CreateFmt(SSocketBindingError,
  477. [Port, StrError(SocketError)]);
  478. Listen(Socket, 5);
  479. if not Assigned(EventLoop) then
  480. raise ESocketError.Create(SSocketNoEventLoopAssigned);
  481. DataAvailableNotifyHandle := EventLoop.SetDataAvailableNotify(Socket,
  482. @ListenerDataAvailable, nil);
  483. FActive := True;
  484. end else
  485. begin
  486. FreeAndNil(FStream);
  487. EventLoop.ClearDataAvailableNotify(DataAvailableNotifyHandle);
  488. DataAvailableNotifyHandle := nil;
  489. end;
  490. end;
  491. end;
  492. end.