ssockets.pp 16 KB

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