ssockets.pp 14 KB

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