ssockets.pp 14 KB

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