ssockets.pp 15 KB

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