mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-12-11 15:10:40 +01:00
* First version, a simple starting point for further development
This commit is contained in:
parent
b3b9175ad0
commit
6c8db02009
245
fcl/net/fpsock.pp
Normal file
245
fcl/net/fpsock.pp
Normal file
@ -0,0 +1,245 @@
|
||||
{
|
||||
$Id$
|
||||
|
||||
Socket components
|
||||
Copyright (c) 2003 by
|
||||
Areca Systems GmbH / Sebastian Guenther, sg@freepascal.org
|
||||
|
||||
See the file COPYING.FPC, included in this distribution,
|
||||
for details about the copyright.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
}
|
||||
|
||||
unit fpSock;
|
||||
|
||||
interface
|
||||
|
||||
uses SysUtils, Sockets, Classes, fpAsync;
|
||||
|
||||
type
|
||||
|
||||
ESocketError = class(Exception)
|
||||
end;
|
||||
|
||||
TSocketComponent = class(TComponent)
|
||||
private
|
||||
FEventLoop: TEventLoop;
|
||||
protected
|
||||
DataAvailableNotifyHandle: Pointer;
|
||||
public
|
||||
destructor Destroy; override;
|
||||
property EventLoop: TEventLoop read FEventLoop write FEventLoop;
|
||||
end;
|
||||
|
||||
TSocketStream = class(THandleStream)
|
||||
private
|
||||
FOnDisconnect: TNotifyEvent;
|
||||
function GetLocalAddress: TSockAddr;
|
||||
function GetPeerAddress: TSockAddr;
|
||||
public
|
||||
destructor Destroy; override;
|
||||
function Read(var Buffer; Count: LongInt): LongInt; override;
|
||||
function Write(const Buffer; Count: LongInt): LongInt; override;
|
||||
|
||||
property LocalAddress: TSockAddr read GetLocalAddress;
|
||||
property PeerAddress: TSockAddr read GetPeerAddress;
|
||||
property OnDisconnect: TNotifyEvent read FOnDisconnect write FOnDisconnect;
|
||||
end;
|
||||
|
||||
// TCP/IP components
|
||||
|
||||
TTCPConnection = class(TSocketComponent)
|
||||
end;
|
||||
|
||||
TTCPClient = class(TTCPConnection)
|
||||
end;
|
||||
|
||||
TCustomTCPServer = class;
|
||||
|
||||
TQueryConnectEvent = procedure(Sender: TCustomTCPServer; Socket: Integer;
|
||||
var DoConnect: Boolean) of object;
|
||||
TConnectEvent = procedure(Sender: TCustomTCPServer;
|
||||
Stream: TSocketStream) of object;
|
||||
|
||||
TCustomTCPServer = class(TTCPConnection)
|
||||
private
|
||||
FActive: Boolean;
|
||||
FPort: Word;
|
||||
FOnQueryConnect: TQueryConnectEvent;
|
||||
FOnConnect: TConnectEvent;
|
||||
procedure SetActive(Value: Boolean);
|
||||
procedure ListenerDataAvailable(Sender: TObject);
|
||||
protected
|
||||
FSocket: Integer;
|
||||
|
||||
function DoQueryConnect(ASocket: Integer): Boolean; virtual;
|
||||
procedure DoConnect(AStream: TSocketStream); virtual;
|
||||
|
||||
//!!!: Interface/bindings list?
|
||||
property Active: Boolean read FActive write SetActive;
|
||||
property Port: Word read FPort write FPort;
|
||||
property OnQueryConnect: TQueryConnectEvent read FOnQueryConnect
|
||||
write FOnQueryConnect;
|
||||
property OnConnect: TConnectEvent read FOnConnect write FOnConnect;
|
||||
end;
|
||||
|
||||
TTCPServer = class(TCustomTCPServer)
|
||||
public
|
||||
property Socket: Integer read FSocket;
|
||||
published
|
||||
property Active;
|
||||
property Port;
|
||||
property OnQueryConnect;
|
||||
property OnConnect;
|
||||
end;
|
||||
|
||||
|
||||
// UDP/IP components
|
||||
|
||||
TUDPBase = class(TSocketComponent)
|
||||
end;
|
||||
|
||||
TUDPClient = class(TUDPBase)
|
||||
end;
|
||||
|
||||
TUDPServer = class(TUDPBase)
|
||||
end;
|
||||
|
||||
|
||||
implementation
|
||||
|
||||
uses Resolve;
|
||||
|
||||
resourcestring
|
||||
SSocketCreationError = 'Could not create socket';
|
||||
SSocketBindingError = 'Could not bind socket to port %d';
|
||||
SSocketAcceptError = 'Connection accept failed';
|
||||
|
||||
destructor TSocketComponent.Destroy;
|
||||
begin
|
||||
if Assigned(DataAvailableNotifyHandle) then
|
||||
begin
|
||||
EventLoop.ClearDataAvailableNotify(DataAvailableNotifyHandle);
|
||||
// Set to nil to be sure that descendant classes don't do something stupid
|
||||
DataAvailableNotifyHandle := nil;
|
||||
end;
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
|
||||
destructor TSocketStream.Destroy;
|
||||
begin
|
||||
FileClose(Handle);
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
function TSocketStream.Read(var Buffer; Count: LongInt): LongInt;
|
||||
begin
|
||||
Result := recv(Handle, Buffer, Count, MSG_NOSIGNAL);
|
||||
if Result = -1 then
|
||||
begin
|
||||
Result := 0;
|
||||
if Assigned(OnDisconnect) then
|
||||
OnDisconnect(Self);
|
||||
end;
|
||||
end;
|
||||
|
||||
function TSocketStream.Write(const Buffer; Count: LongInt): LongInt;
|
||||
begin
|
||||
Result := send(Handle, Buffer, Count, MSG_NOSIGNAL);
|
||||
if Result = -1 then
|
||||
begin
|
||||
Result := 0;
|
||||
if Assigned(OnDisconnect) then
|
||||
OnDisconnect(Self);
|
||||
end;
|
||||
end;
|
||||
|
||||
function TSocketStream.GetLocalAddress: TSockAddr;
|
||||
var
|
||||
len: LongInt;
|
||||
begin
|
||||
len := SizeOf(TSockAddr);
|
||||
if GetSocketName(Handle, Result, len) <> 0 then
|
||||
FillChar(Result, SizeOf(Result), 0);
|
||||
end;
|
||||
|
||||
function TSocketStream.GetPeerAddress: TSockAddr;
|
||||
var
|
||||
len: LongInt;
|
||||
begin
|
||||
len := SizeOf(TSockAddr);
|
||||
if GetPeerName(Handle, Result, len) <> 0 then
|
||||
FillChar(Result, SizeOf(Result), 0);
|
||||
end;
|
||||
|
||||
|
||||
function TCustomTCPServer.DoQueryConnect(ASocket: Integer): Boolean;
|
||||
begin
|
||||
Result := True;
|
||||
if Assigned(OnQueryConnect) then
|
||||
OnQueryConnect(Self, ASocket, Result);
|
||||
end;
|
||||
|
||||
procedure TCustomTCPServer.DoConnect(AStream: TSocketStream);
|
||||
begin
|
||||
if Assigned(OnConnect) then
|
||||
OnConnect(Self, AStream);
|
||||
end;
|
||||
|
||||
procedure TCustomTCPServer.SetActive(Value: Boolean);
|
||||
var
|
||||
Addr: TInetSockAddr;
|
||||
begin
|
||||
if Active <> Value then
|
||||
begin
|
||||
FActive := False;
|
||||
if Value then
|
||||
begin
|
||||
FSocket := Sockets.Socket(AF_INET, SOCK_STREAM, 0);
|
||||
if FSocket = -1 then
|
||||
raise ESocketError.Create(SSocketCreationError);
|
||||
Addr.Family := AF_INET;
|
||||
Addr.Port := ShortHostToNet(Port);
|
||||
Addr.Addr := 0;
|
||||
if not Bind(FSocket, Addr, SizeOf(Addr)) then
|
||||
raise ESocketError.CreateFmt(SSocketBindingError, [Port]);
|
||||
Listen(FSocket, 5);
|
||||
DataAvailableNotifyHandle := EventLoop.SetDataAvailableNotify(FSocket,
|
||||
@ListenerDataAvailable, nil);
|
||||
end else
|
||||
begin
|
||||
FileClose(FSocket);
|
||||
EventLoop.ClearDataAvailableNotify(DataAvailableNotifyHandle);
|
||||
DataAvailableNotifyHandle := nil;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TCustomTCPServer.ListenerDataAvailable(Sender: TObject);
|
||||
var
|
||||
ClientSocket: Integer;
|
||||
Addr: TInetSockAddr;
|
||||
AddrSize: Integer;
|
||||
begin
|
||||
AddrSize := SizeOf(Addr);
|
||||
ClientSocket := Accept(FSocket, Addr, AddrSize);
|
||||
if ClientSocket = -1 then
|
||||
raise ESocketError.Create(SSocketAcceptError);
|
||||
|
||||
if DoQueryConnect(ClientSocket) then
|
||||
DoConnect(TSocketStream.Create(ClientSocket));
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
|
||||
{
|
||||
$Log$
|
||||
Revision 1.1 2003-11-22 11:55:28 sg
|
||||
* First version, a simple starting point for further development
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user