ssockets.pp 14 KB

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