ssockets.pp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  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. Function Read (Var Buffer; Count : Longint) : longint; Override;
  41. Function Write (Const Buffer; Count : Longint) :Longint; Override;
  42. Property SocketOptions : TSocketOptions Read FSocketOptions
  43. Write SetSocketOptions;
  44. end;
  45. TConnectEvent = Procedure (Sender : TObject; Data : TSocketStream) Of Object;
  46. TConnectQuery = Procedure (Sender : TObject; ASocket : Longint; Var Allow : Boolean) of Object;
  47. TSocketServer = Class(TObject)
  48. Private
  49. FOnIdle : TNotifyEvent;
  50. FNonBlocking : Boolean;
  51. FSocket : longint;
  52. FListened : Boolean;
  53. FAccepting : Boolean;
  54. FMaxConnections : Longint;
  55. FQueueSize : Longint;
  56. FOnConnect : TConnectEvent;
  57. FOnConnectQuery : TConnectQuery;
  58. Procedure DoOnIdle;
  59. Protected
  60. FSockType : Longint;
  61. FBound : Boolean;
  62. Procedure DoConnect(ASocket : TSocketStream); Virtual;
  63. Function DoConnectQuery(ASocket : longint): Boolean ;Virtual;
  64. Procedure Bind; Virtual; Abstract;
  65. Function Accept: Longint;Virtual;Abstract;
  66. Function SockToStream (ASocket : Longint) : TSocketStream;Virtual;Abstract;
  67. Procedure Close; Virtual;
  68. Public
  69. Constructor Create(ASocket : Longint);
  70. Destructor Destroy; Override;
  71. Procedure Listen;
  72. Procedure StartAccepting;
  73. Procedure StopAccepting;
  74. Procedure SetNonBlocking;
  75. Property Bound : Boolean Read FBound;
  76. Property MaxConnections : longint Read FMaxConnections Write FMaxConnections;
  77. Property QueueSize : Longint Read FQueueSize Write FQueueSize default 5;
  78. Property OnConnect : TConnectEvent Read FOnConnect Write FOnConnect;
  79. Property OnConnectQuery : TConnectQuery Read FOnConnectQuery Write FOnConnectQuery;
  80. Property OnIdle : TNotifyEvent Read FOnIdle Write FOnIdle;
  81. Property NonBlocking : Boolean Read FNonBlocking;
  82. Property Socket : Longint Read FSocket;
  83. Property SockType : Longint Read FSockType;
  84. end;
  85. TInetServer = Class(TSocketServer)
  86. Protected
  87. FAddr : TINetSockAddr;
  88. Function SockToStream (ASocket : Longint) : TSocketStream;Override;
  89. Function Accept : Longint;override;
  90. FPort : Word;
  91. Public
  92. Procedure Bind; Override;
  93. Constructor Create(APort: Word);
  94. Property Port : Word Read FPort;
  95. end;
  96. {$ifndef win32}
  97. TUnixServer = Class(TSocketServer)
  98. Private
  99. FUnixAddr : TUnixSockAddr;
  100. FFileName : String;
  101. Protected
  102. Procedure Bind; Override;
  103. Function Accept : Longint;override;
  104. Function SockToStream (ASocket : Longint) : TSocketStream;Override;
  105. Procedure Close; override;
  106. Public
  107. Constructor Create(AFileName : String);
  108. Property FileName : String Read FFileName;
  109. end;
  110. {$endif}
  111. TInetSocket = Class(TSocketStream)
  112. Private
  113. FHost : String;
  114. FPort : Word;
  115. Protected
  116. Procedure DoConnect(ASocket : longint); Virtual;
  117. Public
  118. Constructor Create(ASocket : longint); Override; {$ifndef ver1_0}Overload;{$endif}
  119. Constructor Create(const AHost: String; APort: Word); {$ifndef ver1_0}Overload;{$endif}
  120. Property Host : String Read FHost;
  121. Property Port : Word Read FPort;
  122. end;
  123. {$ifndef win32}
  124. TUnixSocket = Class(TSocketStream)
  125. Private
  126. FFileName : String;
  127. Protected
  128. Procedure DoConnect(ASocket : longint); Virtual;
  129. Public
  130. Constructor Create(ASocket : Longint); {$ifndef ver1_0}Overload;{$endif}
  131. Constructor Create(AFileName : String); {$ifndef ver1_0}Overload;{$endif}
  132. Property FileName : String Read FFileName;
  133. end;
  134. {$endif}
  135. Implementation
  136. uses
  137. {$ifdef unix}
  138. {$ifdef ver1_0}
  139. Linux,
  140. {$else}
  141. Unix,
  142. {$endif}
  143. {$endif}
  144. resolve
  145. ;
  146. Const
  147. SocketWouldBlock = -2;
  148. { ---------------------------------------------------------------------
  149. ESocketError
  150. ---------------------------------------------------------------------}
  151. resourcestring
  152. strHostNotFound = 'Host name resolution for "%s" failed.';
  153. strSocketCreationFailed = 'Creation of socket failed: %s';
  154. strSocketBindFailed = 'Binding of socket failed: %s';
  155. strSocketListenFailed = 'Listening on port #%d failed: %s';
  156. strSocketConnectFailed = 'Connect to %s failed.';
  157. strSocketAcceptFailed = 'Could not accept a client connection: %s';
  158. strSocketAcceptWouldBlock = 'Accept would block on socket: %d';
  159. constructor ESocketError.Create(ACode: TSocketErrorType; const MsgArgs: array of const);
  160. var
  161. s: String;
  162. begin
  163. Code := ACode;
  164. case ACode of
  165. seHostNotFound : s := strHostNotFound;
  166. seCreationFailed: s := strSocketCreationFailed;
  167. seBindFailed : s := strSocketBindFailed;
  168. seListenFailed : s := strSocketListenFailed;
  169. seConnectFailed : s := strSocketConnectFailed;
  170. seAcceptFailed : s := strSocketAcceptFailed;
  171. seAcceptWouldBLock : S:= strSocketAcceptWouldBlock;
  172. end;
  173. s := Format(s, MsgArgs);
  174. inherited Create(s);
  175. end;
  176. { ---------------------------------------------------------------------
  177. TSocketStream
  178. ---------------------------------------------------------------------}
  179. Constructor TSocketStream.Create (AHandle : Longint);
  180. begin
  181. Inherited Create(AHandle);
  182. GetSockOptions;
  183. end;
  184. destructor TSocketStream.Destroy;
  185. begin
  186. FileClose(Handle);
  187. inherited Destroy;
  188. end;
  189. Procedure TSocketStream.GetSockOptions;
  190. begin
  191. end;
  192. Procedure TSocketStream.SetSocketOptions(Value : TSocketOptions);
  193. begin
  194. end;
  195. Function TSocketStream.Seek(Offset: Longint; Origin: Word): Longint;
  196. begin
  197. Result:=0;
  198. end;
  199. Function TSocketStream.Read (Var Buffer; Count : Longint) : longint;
  200. Var
  201. Flags : longint;
  202. begin
  203. Flags:=0;
  204. Result:=recv(handle,Buffer,count,flags);
  205. end;
  206. Function TSocketStream.Write (Const Buffer; Count : Longint) :Longint;
  207. Var
  208. Flags : longint;
  209. begin
  210. Flags:=0;
  211. Result:=send(handle,Buffer,count,flags);
  212. end;
  213. { ---------------------------------------------------------------------
  214. TSocketServer
  215. ---------------------------------------------------------------------}
  216. Constructor TSocketServer.Create(ASocket : Longint);
  217. begin
  218. FSocket:=ASocket;
  219. FQueueSize :=5;
  220. end;
  221. Destructor TSocketServer.Destroy;
  222. begin
  223. Close;
  224. end;
  225. Procedure TSocketServer.Close;
  226. begin
  227. If FSocket<>-1 Then
  228. FileClose(FSocket);
  229. FSocket:=-1;
  230. end;
  231. Procedure TSocketServer.Listen;
  232. begin
  233. If Not FBound then
  234. Bind;
  235. If Not Sockets.Listen(FSocket,FQueueSize) then
  236. Raise ESocketError.Create(seListenFailed,[FSocket]);
  237. end;
  238. Procedure TSocketServer.StartAccepting;
  239. Var
  240. NoConnections,
  241. NewSocket : longint;
  242. Stream : TSocketStream;
  243. begin
  244. FAccepting := True;
  245. Listen;
  246. Repeat
  247. Repeat
  248. Try
  249. NewSocket:=Accept;
  250. If NewSocket>=0 then
  251. begin
  252. Inc (NoConnections);
  253. If DoConnectQuery(NewSocket) Then
  254. begin
  255. Stream:=SockToStream(NewSocket);
  256. DoConnect(Stream);
  257. end
  258. end
  259. except
  260. On E : ESocketError do
  261. If E.Code=seAcceptWouldBlock then
  262. begin
  263. DoOnIdle;
  264. NewSocket:=-1;
  265. end;
  266. else
  267. Raise;
  268. end;
  269. Until (NewSocket>=0) or (Not NonBlocking);
  270. Until Not (FAccepting) or ((FMaxConnections<>-1) and (NoConnections>=FMaxConnections));
  271. end;
  272. Procedure TSocketServer.StopAccepting;
  273. begin
  274. FAccepting:=False;
  275. end;
  276. Procedure TSocketServer.DoOnIdle;
  277. begin
  278. If Assigned(FOnIdle) then
  279. FOnIdle(Self);
  280. end;
  281. Procedure TSocketServer.DoConnect(ASocket : TSocketStream);
  282. begin
  283. If Assigned(FOnConnect) Then
  284. FOnConnect(Self,ASocket);
  285. end;
  286. Function TSocketServer.DoConnectQuery(ASocket : Longint) : Boolean;
  287. begin
  288. Result:=True;
  289. If Assigned(FOnConnectQuery) then
  290. FOnConnectQuery(Self,ASocket,Result);
  291. end;
  292. Procedure TSocketServer.SetNonBlocking;
  293. begin
  294. {$ifndef win32}
  295. fcntl(FSocket,F_SETFL,OPEN_NONBLOCK);
  296. {$endif}
  297. FNonBlocking:=True;
  298. end;
  299. { ---------------------------------------------------------------------
  300. TInetServer
  301. ---------------------------------------------------------------------}
  302. Constructor TInetServer.Create(APort: Word);
  303. Var S : longint;
  304. begin
  305. FPort:=APort;
  306. S:=Sockets.Socket(AF_INET,SOCK_STREAM,0);
  307. If S=-1 Then
  308. Raise ESocketError.Create(seCreationFailed,[Format('%d',[APort])]);
  309. Inherited Create(S);
  310. end;
  311. Procedure TInetServer.Bind;
  312. begin
  313. Faddr.family := AF_INET;
  314. Faddr.port := ShortHostToNet(FPort);
  315. Faddr.addr := 0;
  316. if not Sockets.Bind(FSocket, FAddr, Sizeof(FAddr)) then
  317. raise ESocketError.Create(seBindFailed, [IntToStr(FPort)]);
  318. FBound:=True;
  319. end;
  320. Function TInetServer.SockToStream (ASocket : Longint) : TSocketStream;
  321. begin
  322. Result:=TInetSocket.Create(ASocket);
  323. (Result as TInetSocket).FHost:='';
  324. (Result as TInetSocket).FPort:=FPort;
  325. end;
  326. Function TInetServer.Accept : Longint;
  327. Var l : longint;
  328. begin
  329. L:=SizeOf(FAddr);
  330. Result:=Sockets.Accept(Socket,Faddr,L);
  331. If Result<0 then
  332. {$ifndef win32}
  333. If SocketError={$ifdef ver1_0}Sys_EWOULDBLOCK{$else}ESysEWOULDBLOCK{$endif} then
  334. Raise ESocketError.Create(seAcceptWouldBlock,[socket])
  335. else
  336. {$endif}
  337. Raise ESocketError.Create(seAcceptFailed,[socket]);
  338. end;
  339. { ---------------------------------------------------------------------
  340. TUnixServer
  341. ---------------------------------------------------------------------}
  342. {$ifndef win32}
  343. Constructor TUnixServer.Create(AFileName : String);
  344. Var S : Longint;
  345. begin
  346. FFileName:=AFileName;
  347. S:=Sockets.Socket(AF_UNIX,SOCK_STREAM,0);
  348. If S=-1 then
  349. Raise ESocketError.Create(seCreationFailed,[AFileName])
  350. else
  351. Inherited Create(S);
  352. end;
  353. Procedure TUnixServer.Close;
  354. begin
  355. Inherited Close;
  356. DeleteFile(FFileName);
  357. FFileName:='';
  358. end;
  359. Procedure TUnixServer.Bind;
  360. var
  361. AddrLen : longint;
  362. begin
  363. Str2UnixSockAddr(FFilename,FUnixAddr,AddrLen);
  364. If Not Sockets.Bind(Socket,FUnixAddr,AddrLen) then
  365. Raise ESocketError.Create(seBindFailed,[FFileName]);
  366. FBound:=True;
  367. end;
  368. Function TUnixServer.Accept : Longint;
  369. Var L : longint;
  370. begin
  371. L:=Length(FFileName);
  372. Result:=Sockets.Accept(Socket,FUnixAddr,L);
  373. If Result<0 then
  374. If SocketError={$ifdef ver1_0}Sys_EWOULDBLOCK{$else}ESysEWOULDBLOCK{$endif} then
  375. Raise ESocketError.Create(seAcceptWouldBlock,[socket])
  376. else
  377. Raise ESocketError.Create(seAcceptFailed,[socket]);
  378. end;
  379. Function TUnixServer.SockToStream (ASocket : Longint) : TSocketStream;
  380. begin
  381. Result:=TUnixSocket.Create(ASocket);
  382. (Result as TUnixSocket).FFileName:=FFileName;
  383. end;
  384. {$endif}
  385. { ---------------------------------------------------------------------
  386. TInetSocket
  387. ---------------------------------------------------------------------}
  388. Constructor TInetSocket.Create(ASocket : Longint);
  389. begin
  390. Inherited Create(ASocket);
  391. end;
  392. Constructor TInetSocket.Create(const AHost: String; APort: Word);
  393. Var
  394. S : Longint;
  395. begin
  396. FHost:=AHost;
  397. FPort:=APort;
  398. S:=Socket(AF_INET,SOCK_STREAM,0);
  399. DoConnect(S);
  400. Inherited Create(S);
  401. end;
  402. Procedure TInetSocket.DoConnect(ASocket : Longint);
  403. Var
  404. TheHost: THostResolver;
  405. A : THostAddr;
  406. addr: TInetSockAddr;
  407. begin
  408. With THostResolver.Create(Nil) do
  409. try
  410. If Not NameLookup(FHost) then
  411. raise ESocketError.Create(seHostNotFound, [FHost]);
  412. A:=HostAddress;
  413. finally
  414. free;
  415. end;
  416. addr.family := AF_INET;
  417. addr.port := ShortHostToNet(FPort);
  418. addr.addr := Longint(A);
  419. If not Sockets.Connect(ASocket, addr, sizeof(addr)) then
  420. raise ESocketError.Create(seConnectFailed, [Format('%s:%d',[FHost, FPort])]);
  421. end;
  422. { ---------------------------------------------------------------------
  423. TUnixSocket
  424. ---------------------------------------------------------------------}
  425. {$ifndef win32}
  426. Constructor TUnixSocket.Create(ASocket : Longint);
  427. begin
  428. Inherited Create(ASocket);
  429. end;
  430. Constructor TUnixSocket.Create(AFileName : String);
  431. Var S : Longint;
  432. begin
  433. FFileName:=AFileName;
  434. S:=Socket(AF_UNIX,SOCK_STREAM,0);
  435. DoConnect(S);
  436. Inherited Create(S);
  437. end;
  438. Procedure TUnixSocket.DoConnect(ASocket : longint);
  439. Var
  440. UnixAddr : TUnixSockAddr;
  441. AddrLen : longint;
  442. begin
  443. Str2UnixSockAddr(FFilename,UnixAddr,AddrLen);
  444. If Not Connect(ASocket,UnixAddr,AddrLen) then
  445. Raise ESocketError.Create(seConnectFailed,[FFilename]);
  446. end;
  447. {$endif}
  448. end.
  449. {
  450. $Log$
  451. Revision 1.17 2003-03-11 13:15:40 michael
  452. + Initial version working on Win32. Needs some further work
  453. Revision 1.16 2003/03/10 21:42:39 michael
  454. + TSocketStream now uses recv/sendto instead of read/write
  455. Revision 1.15 2003/03/07 20:57:09 michael
  456. + Use resolve unit instead of inet unit.
  457. Revision 1.14 2002/12/18 18:39:14 peter
  458. * renamed error constants for 1.1
  459. Revision 1.13 2002/12/12 17:53:49 peter
  460. * add FAccepting:=true to StartAccepting
  461. Revision 1.12 2002/09/07 15:15:25 peter
  462. * old logs removed and tabs fixed
  463. Revision 1.11 2002/05/31 11:31:46 marco
  464. * 1.0.x Renamefest for FCL. Fixed some oddities in 1.1 too
  465. }