ssockets.pp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554
  1. {
  2. $Id$
  3. This file is part of the Free Component Library (FCL)
  4. Copyright (c) 1999-2000 by the Free Pascal development team
  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. {$MODE objfpc}
  12. unit ssockets;
  13. interface
  14. uses SysUtils, Classes, sockets;
  15. type
  16. TSocketErrorType = (
  17. seHostNotFound,
  18. seCreationFailed,
  19. seBindFailed,
  20. seListenFailed,
  21. seConnectFailed,
  22. seAcceptFailed,
  23. seAcceptWouldBlock);
  24. TSocketOption = (soDebug,soReuseAddr,soKeepAlive,soDontRoute,soBroadcast,
  25. soOOBinline);
  26. TSocketOptions = Set of TSocketOption;
  27. ESocketError = class(Exception)
  28. Code: TSocketErrorType;
  29. constructor Create(ACode: TSocketErrorType; const MsgArgs: array of const);
  30. end;
  31. TSocketStream = class(THandleStream)
  32. Private
  33. FSocketOptions : TSocketOptions;
  34. Procedure GetSockOptions;
  35. Procedure SetSocketOptions(Value : TSocketOptions);
  36. Public
  37. Constructor Create (AHandle : Longint);virtual;
  38. destructor Destroy; override;
  39. function Seek(Offset: Longint; Origin: Word): Longint; override;
  40. Property SocketOptions : TSocketOptions Read FSocketOptions
  41. Write SetSocketOptions;
  42. end;
  43. TConnectEvent = Procedure (Sender : TObject; Data : TSocketStream) Of Object;
  44. TConnectQuery = Procedure (Sender : TObject; ASocket : Longint; Var Allow : Boolean) of Object;
  45. TSocketServer = Class(TObject)
  46. Private
  47. FOnIdle : TNotifyEvent;
  48. FNonBlocking : Boolean;
  49. FSocket : longint;
  50. FListened : Boolean;
  51. FAccepting : Boolean;
  52. FMaxConnections : Longint;
  53. FQueueSize : Longint;
  54. FOnConnect : TConnectEvent;
  55. FOnConnectQuery : TConnectQuery;
  56. Procedure DoOnIdle;
  57. Protected
  58. FSockType : Longint;
  59. FBound : Boolean;
  60. Procedure DoConnect(ASocket : TSocketStream); Virtual;
  61. Function DoConnectQuery(ASocket : longint): Boolean ;Virtual;
  62. Procedure Bind; Virtual; Abstract;
  63. Function Accept: Longint;Virtual;Abstract;
  64. Function SockToStream (ASocket : Longint) : TSocketStream;Virtual;Abstract;
  65. Procedure Close; Virtual;
  66. Public
  67. Constructor Create(ASocket : Longint);
  68. Destructor Destroy; Override;
  69. Procedure Listen;
  70. Procedure StartAccepting;
  71. Procedure StopAccepting;
  72. Procedure SetNonBlocking;
  73. Property Bound : Boolean Read FBound;
  74. Property MaxConnections : longint Read FMaxConnections Write FMaxConnections;
  75. Property QueueSize : Longint Read FQueueSize Write FQueueSize default 5;
  76. Property OnConnect : TConnectEvent Read FOnConnect Write FOnConnect;
  77. Property OnConnectQuery : TConnectQuery Read FOnConnectQuery Write FOnConnectQuery;
  78. Property OnIdle : TNotifyEvent Read FOnIdle Write FOnIdle;
  79. Property NonBlocking : Boolean Read FNonBlocking;
  80. Property Socket : Longint Read FSocket;
  81. Property SockType : Longint Read FSockType;
  82. end;
  83. TInetServer = Class(TSocketServer)
  84. Protected
  85. FAddr : TINetSockAddr;
  86. Function SockToStream (ASocket : Longint) : TSocketStream;Override;
  87. Function Accept : Longint;override;
  88. FPort : Word;
  89. Public
  90. Procedure Bind; Override;
  91. Constructor Create(APort: Word);
  92. Property Port : Word Read FPort;
  93. end;
  94. TUnixServer = Class(TSocketServer)
  95. Private
  96. FUnixAddr : TUnixSockAddr;
  97. FFileName : String;
  98. Protected
  99. Procedure Bind; Override;
  100. Function Accept : Longint;override;
  101. Function SockToStream (ASocket : Longint) : TSocketStream;Override;
  102. Procedure Close; override;
  103. Public
  104. Constructor Create(AFileName : String);
  105. Property FileName : String Read FFileName;
  106. end;
  107. TInetSocket = Class(TSocketStream)
  108. Private
  109. FHost : String;
  110. FPort : Word;
  111. Protected
  112. Procedure DoConnect(ASocket : longint); Virtual;
  113. Public
  114. Constructor Create(ASocket : longint); Override; {$ifndef ver1_0}Overload;{$endif}
  115. Constructor Create(const AHost: String; APort: Word); {$ifndef ver1_0}Overload;{$endif}
  116. Property Host : String Read FHost;
  117. Property Port : Word Read FPort;
  118. end;
  119. TUnixSocket = Class(TSocketStream)
  120. Private
  121. FFileName : String;
  122. Protected
  123. Procedure DoConnect(ASocket : longint); Virtual;
  124. Public
  125. Constructor Create(ASocket : Longint); {$ifndef ver1_0}Overload;{$endif}
  126. Constructor Create(AFileName : String); {$ifndef ver1_0}Overload;{$endif}
  127. Property FileName : String Read FFileName;
  128. end;
  129. Implementation
  130. uses
  131. {$ifdef linux}
  132. {$ifndef freebsd}
  133. {$ifdef ver1_0}
  134. Linux,
  135. {$else}
  136. Unix,
  137. {$endif}
  138. {$endif}
  139. {$endif}
  140. {$ifdef freebsd}
  141. {$ifdef ver1_0}
  142. Linux,
  143. {$else}
  144. Unix,
  145. {$endif}
  146. {$endif}
  147. inet
  148. ;
  149. Const
  150. SocketWouldBlock = -2;
  151. { ---------------------------------------------------------------------
  152. ESocketError
  153. ---------------------------------------------------------------------}
  154. resourcestring
  155. strHostNotFound = 'Host name resolution for "%s" failed.';
  156. strSocketCreationFailed = 'Creation of socket failed: %s';
  157. strSocketBindFailed = 'Binding of socket failed: %s';
  158. strSocketListenFailed = 'Listening on port #%d failed: %s';
  159. strSocketConnectFailed = 'Connect to %s failed.';
  160. strSocketAcceptFailed = 'Could not accept a client connection: %s';
  161. strSocketAcceptWouldBlock = 'Accept would block on socket: %d';
  162. constructor ESocketError.Create(ACode: TSocketErrorType; const MsgArgs: array of const);
  163. var
  164. s: String;
  165. begin
  166. Code := ACode;
  167. case ACode of
  168. seHostNotFound : s := strHostNotFound;
  169. seCreationFailed: s := strSocketCreationFailed;
  170. seBindFailed : s := strSocketBindFailed;
  171. seListenFailed : s := strSocketListenFailed;
  172. seConnectFailed : s := strSocketConnectFailed;
  173. seAcceptFailed : s := strSocketAcceptFailed;
  174. seAcceptWouldBLock : S:= strSocketAcceptWouldBlock;
  175. end;
  176. s := Format(s, MsgArgs);
  177. inherited Create(s);
  178. end;
  179. { ---------------------------------------------------------------------
  180. TSocketStream
  181. ---------------------------------------------------------------------}
  182. Constructor TSocketStream.Create (AHandle : Longint);
  183. begin
  184. Inherited Create(AHandle);
  185. GetSockOptions;
  186. end;
  187. destructor TSocketStream.Destroy;
  188. begin
  189. FileClose(Handle);
  190. inherited Destroy;
  191. end;
  192. Procedure TSocketStream.GetSockOptions;
  193. begin
  194. end;
  195. Procedure TSocketStream.SetSocketOptions(Value : TSocketOptions);
  196. begin
  197. end;
  198. Function TSocketStream.Seek(Offset: Longint; Origin: Word): Longint;
  199. begin
  200. Result:=0;
  201. end;
  202. { ---------------------------------------------------------------------
  203. TSocketServer
  204. ---------------------------------------------------------------------}
  205. Constructor TSocketServer.Create(ASocket : Longint);
  206. begin
  207. FSocket:=ASocket;
  208. FQueueSize :=5;
  209. end;
  210. Destructor TSocketServer.Destroy;
  211. begin
  212. Close;
  213. end;
  214. Procedure TSocketServer.Close;
  215. begin
  216. If FSocket<>-1 Then
  217. FileClose(FSocket);
  218. FSocket:=-1;
  219. end;
  220. Procedure TSocketServer.Listen;
  221. begin
  222. If Not FBound then
  223. Bind;
  224. If Not Sockets.Listen(FSocket,FQueueSize) then
  225. Raise ESocketError.Create(seListenFailed,[FSocket]);
  226. end;
  227. Procedure TSocketServer.StartAccepting;
  228. Var
  229. NoConnections,
  230. NewSocket : longint;
  231. Stream : TSocketStream;
  232. begin
  233. Listen;
  234. Repeat
  235. Repeat
  236. Try
  237. NewSocket:=Accept;
  238. If NewSocket>=0 then
  239. begin
  240. Inc (NoConnections);
  241. If DoConnectQuery(NewSocket) Then
  242. begin
  243. Stream:=SockToStream(NewSocket);
  244. DoConnect(Stream);
  245. end
  246. end
  247. except
  248. On E : ESocketError do
  249. If E.Code=seAcceptWouldBlock then
  250. begin
  251. DoOnIdle;
  252. NewSocket:=-1;
  253. end;
  254. else
  255. Raise;
  256. end;
  257. Until (NewSocket>=0) or (Not NonBlocking);
  258. Until Not (FAccepting) or ((FMaxConnections<>-1) and (NoConnections>=FMaxConnections));
  259. end;
  260. Procedure TSocketServer.StopAccepting;
  261. begin
  262. FAccepting:=False;
  263. end;
  264. Procedure TSocketServer.DoOnIdle;
  265. begin
  266. If Assigned(FOnIdle) then
  267. FOnIdle(Self);
  268. end;
  269. Procedure TSocketServer.DoConnect(ASocket : TSocketStream);
  270. begin
  271. If Assigned(FOnConnect) Then
  272. FOnConnect(Self,ASocket);
  273. end;
  274. Function TSocketServer.DoConnectQuery(ASocket : Longint) : Boolean;
  275. begin
  276. Result:=True;
  277. If Assigned(FOnConnectQuery) then
  278. FOnConnectQuery(Self,ASocket,Result);
  279. end;
  280. Procedure TSocketServer.SetNonBlocking;
  281. begin
  282. fcntl(FSocket,F_SETFL,OPEN_NONBLOCK);
  283. FNonBlocking:=True;
  284. end;
  285. { ---------------------------------------------------------------------
  286. TInetServer
  287. ---------------------------------------------------------------------}
  288. Constructor TInetServer.Create(APort: Word);
  289. Var S : longint;
  290. begin
  291. FPort:=APort;
  292. S:=Sockets.Socket(AF_INET,SOCK_STREAM,0);
  293. If S=-1 Then
  294. Raise ESocketError.Create(seCreationFailed,[Format('%d',[APort])]);
  295. Inherited Create(S);
  296. end;
  297. Procedure TInetServer.Bind;
  298. begin
  299. Faddr.family := AF_INET;
  300. Faddr.port := ShortHostToNet(FPort);
  301. Faddr.addr := 0;
  302. if not Sockets.Bind(FSocket, FAddr, Sizeof(FAddr)) then
  303. raise ESocketError.Create(seBindFailed, [IntToStr(FPort)]);
  304. FBound:=True;
  305. end;
  306. Function TInetServer.SockToStream (ASocket : Longint) : TSocketStream;
  307. begin
  308. Result:=TInetSocket.Create(ASocket);
  309. (Result as TInetSocket).FHost:='';
  310. (Result as TInetSocket).FPort:=FPort;
  311. end;
  312. Function TInetServer.Accept : Longint;
  313. Var l : longint;
  314. begin
  315. L:=SizeOf(FAddr);
  316. Result:=Sockets.Accept(Socket,Faddr,L);
  317. If Result<0 then
  318. If SocketError=Sys_EWOULDBLOCK then
  319. Raise ESocketError.Create(seAcceptWouldBlock,[socket])
  320. else
  321. Raise ESocketError.Create(seAcceptFailed,[socket]);
  322. end;
  323. { ---------------------------------------------------------------------
  324. TUnixServer
  325. ---------------------------------------------------------------------}
  326. Constructor TUnixServer.Create(AFileName : String);
  327. Var S : Longint;
  328. begin
  329. FFileName:=AFileName;
  330. S:=Sockets.Socket(AF_UNIX,SOCK_STREAM,0);
  331. If S=-1 then
  332. Raise ESocketError.Create(seCreationFailed,[AFileName])
  333. else
  334. Inherited Create(S);
  335. end;
  336. Procedure TUnixServer.Close;
  337. begin
  338. Inherited Close;
  339. DeleteFile(FFileName);
  340. FFileName:='';
  341. end;
  342. Procedure TUnixServer.Bind;
  343. var
  344. AddrLen : longint;
  345. begin
  346. Str2UnixSockAddr(FFilename,FUnixAddr,AddrLen);
  347. If Not Sockets.Bind(Socket,FUnixAddr,AddrLen) then
  348. Raise ESocketError.Create(seBindFailed,[FFileName]);
  349. FBound:=True;
  350. end;
  351. Function TUnixServer.Accept : Longint;
  352. Var L : longint;
  353. begin
  354. L:=Length(FFileName);
  355. Result:=Sockets.Accept(Socket,FUnixAddr,L);
  356. If Result<0 then
  357. If SocketError=Sys_EWOULDBLOCK then
  358. Raise ESocketError.Create(seAcceptWouldBlock,[socket])
  359. else
  360. Raise ESocketError.Create(seAcceptFailed,[socket]);
  361. end;
  362. Function TUnixServer.SockToStream (ASocket : Longint) : TSocketStream;
  363. begin
  364. Result:=TUnixSocket.Create(ASocket);
  365. (Result as TUnixSocket).FFileName:=FFileName;
  366. end;
  367. { ---------------------------------------------------------------------
  368. TInetSocket
  369. ---------------------------------------------------------------------}
  370. Constructor TInetSocket.Create(ASocket : Longint);
  371. begin
  372. Inherited Create(ASocket);
  373. end;
  374. Constructor TInetSocket.Create(const AHost: String; APort: Word);
  375. Var
  376. S : Longint;
  377. begin
  378. FHost:=AHost;
  379. FPort:=APort;
  380. S:=Socket(AF_INET,SOCK_STREAM,0);
  381. DoConnect(S);
  382. Inherited Create(S);
  383. end;
  384. Procedure TInetSocket.DoConnect(ASocket : Longint);
  385. Var
  386. TheHost: THost;
  387. addr: TInetSockAddr;
  388. begin
  389. TheHost.NameLookup(FHost);
  390. if TheHost.LastError <> 0 then
  391. raise ESocketError.Create(seHostNotFound, [FHost]);
  392. addr.family := AF_INET;
  393. addr.port := ShortHostToNet(FPort);
  394. addr.addr := HostToNet(LongInt(TheHost.IPAddress));
  395. If not Sockets.Connect(ASocket, addr, sizeof(addr)) then
  396. raise ESocketError.Create(seConnectFailed, [Format('%s:%d',[FHost, FPort])]);
  397. end;
  398. { ---------------------------------------------------------------------
  399. TUnixSocket
  400. ---------------------------------------------------------------------}
  401. Constructor TUnixSocket.Create(ASocket : Longint);
  402. begin
  403. Inherited Create(ASocket);
  404. end;
  405. Constructor TUnixSocket.Create(AFileName : String);
  406. Var S : Longint;
  407. begin
  408. FFileName:=AFileName;
  409. S:=Socket(AF_UNIX,SOCK_STREAM,0);
  410. DoConnect(S);
  411. Inherited Create(S);
  412. end;
  413. Procedure TUnixSocket.DoConnect(ASocket : longint);
  414. Var
  415. UnixAddr : TUnixSockAddr;
  416. AddrLen : longint;
  417. begin
  418. Str2UnixSockAddr(FFilename,UnixAddr,AddrLen);
  419. If Not Connect(ASocket,UnixAddr,AddrLen) then
  420. Raise ESocketError.Create(seConnectFailed,[FFilename]);
  421. end;
  422. end.
  423. {
  424. $Log$
  425. Revision 1.10 2001-12-17 21:03:55 sg
  426. * Fixed merging mistake of my last commit :/
  427. Revision 1.9 2001/12/17 20:21:35 sg
  428. * Some cosmetic improvements (fixed wrong method visibilities etc.)
  429. * TSocketStream now closes the underlying file handle upon destruction
  430. Revision 1.8 2001/11/24 20:59:13 carl
  431. * fix compilation problems for version 1.0.x branch
  432. Revision 1.7 2001/11/20 18:53:29 peter
  433. * overload fix
  434. Revision 1.6 2001/04/20 18:50:00 marco
  435. * FreeVSD fixy
  436. Revision 1.5 2001/04/08 11:26:03 peter
  437. * update so it can be compiled by both 1.0.x and 1.1
  438. Revision 1.4 2001/01/21 20:45:09 marco
  439. * Rename fest II FCL version.
  440. Revision 1.3 2000/11/17 13:40:53 sg
  441. * Fixed header and log section
  442. Revision 1.2 2000/07/13 11:33:00 michael
  443. + removed logs
  444. }