|
@@ -1,7 +1,7 @@
|
|
{
|
|
{
|
|
$Id$
|
|
$Id$
|
|
|
|
|
|
- Socket components
|
|
|
|
|
|
+ Socket communication components
|
|
Copyright (c) 2003 by
|
|
Copyright (c) 2003 by
|
|
Areca Systems GmbH / Sebastian Guenther, [email protected]
|
|
Areca Systems GmbH / Sebastian Guenther, [email protected]
|
|
|
|
|
|
@@ -17,7 +17,7 @@ unit fpSock;
|
|
|
|
|
|
interface
|
|
interface
|
|
|
|
|
|
-uses SysUtils, Sockets, Classes, fpAsync;
|
|
|
|
|
|
+uses Errors, SysUtils, Sockets, Classes, fpAsync, Resolve;
|
|
|
|
|
|
type
|
|
type
|
|
|
|
|
|
@@ -27,10 +27,7 @@ type
|
|
TSocketComponent = class(TComponent)
|
|
TSocketComponent = class(TComponent)
|
|
private
|
|
private
|
|
FEventLoop: TEventLoop;
|
|
FEventLoop: TEventLoop;
|
|
- protected
|
|
|
|
- DataAvailableNotifyHandle: Pointer;
|
|
|
|
public
|
|
public
|
|
- destructor Destroy; override;
|
|
|
|
property EventLoop: TEventLoop read FEventLoop write FEventLoop;
|
|
property EventLoop: TEventLoop read FEventLoop write FEventLoop;
|
|
end;
|
|
end;
|
|
|
|
|
|
@@ -39,6 +36,8 @@ type
|
|
FOnDisconnect: TNotifyEvent;
|
|
FOnDisconnect: TNotifyEvent;
|
|
function GetLocalAddress: TSockAddr;
|
|
function GetLocalAddress: TSockAddr;
|
|
function GetPeerAddress: TSockAddr;
|
|
function GetPeerAddress: TSockAddr;
|
|
|
|
+ protected
|
|
|
|
+ procedure Disconnected; virtual;
|
|
public
|
|
public
|
|
destructor Destroy; override;
|
|
destructor Destroy; override;
|
|
function Read(var Buffer; Count: LongInt): LongInt; override;
|
|
function Read(var Buffer; Count: LongInt): LongInt; override;
|
|
@@ -49,87 +48,165 @@ type
|
|
property OnDisconnect: TNotifyEvent read FOnDisconnect write FOnDisconnect;
|
|
property OnDisconnect: TNotifyEvent read FOnDisconnect write FOnDisconnect;
|
|
end;
|
|
end;
|
|
|
|
|
|
- // TCP/IP components
|
|
|
|
|
|
|
|
- TTCPConnection = class(TSocketComponent)
|
|
|
|
|
|
+ // Connection-based sockets
|
|
|
|
+
|
|
|
|
+ TConnectionBasedSocket = class(TSocketComponent)
|
|
|
|
+ protected
|
|
|
|
+ FStream: TSocketStream;
|
|
|
|
+ FActive: Boolean;
|
|
|
|
+ procedure SetActive(Value: Boolean); virtual; abstract;
|
|
|
|
+ property Active: Boolean read FActive write SetActive;
|
|
|
|
+ property Stream: TSocketStream read FStream;
|
|
|
|
+ public
|
|
|
|
+ destructor Destroy; override;
|
|
end;
|
|
end;
|
|
|
|
|
|
- TTCPClient = class(TTCPConnection)
|
|
|
|
|
|
+
|
|
|
|
+ TConnectionState = (
|
|
|
|
+ connDisconnected,
|
|
|
|
+ connResolving,
|
|
|
|
+ connConnecting,
|
|
|
|
+ connConnected);
|
|
|
|
+
|
|
|
|
+ TClientConnectionSocket = class;
|
|
|
|
+
|
|
|
|
+ TConnectionStateChangeEvent = procedure(Sender: TClientConnectionSocket;
|
|
|
|
+ OldState, NewState: TConnectionState) of object;
|
|
|
|
+
|
|
|
|
+ TClientConnectionSocket = class(TConnectionBasedSocket)
|
|
|
|
+ private
|
|
|
|
+ FOnStateChange: TConnectionStateChangeEvent;
|
|
|
|
+ FRetries: Integer;
|
|
|
|
+ FRetryDelay: Integer; // Delay between retries in ms
|
|
|
|
+ RetryCounter: Integer;
|
|
|
|
+ RetryTimerNotifyHandle: Pointer;
|
|
|
|
+ CanWriteNotifyHandle: Pointer;
|
|
|
|
+ procedure RetryTimerNotify(Sender: TObject);
|
|
|
|
+ procedure SocketCanWrite(Sender: TObject);
|
|
|
|
+ protected
|
|
|
|
+ FConnectionState: TConnectionState;
|
|
|
|
+
|
|
|
|
+ procedure CreateSocket; virtual; abstract;
|
|
|
|
+ procedure DoResolve; virtual;
|
|
|
|
+ procedure DoConnect; virtual;
|
|
|
|
+ function GetPeerName: String; virtual; abstract;
|
|
|
|
+
|
|
|
|
+ procedure SetActive(Value: Boolean); override;
|
|
|
|
+ procedure SetConnectionState(NewState: TConnectionState);
|
|
|
|
+
|
|
|
|
+ property ConnectionState: TConnectionState read FConnectionState;
|
|
|
|
+ property Retries: Integer read FRetries write FRetries default 0;
|
|
|
|
+ property RetryDelay: Integer read FRetryDelay write FRetryDelay default 500;
|
|
|
|
+ property OnConnectionStateChange: TConnectionStateChangeEvent
|
|
|
|
+ read FOnStateChange write FOnStateChange;
|
|
|
|
+ public
|
|
|
|
+ constructor Create(AOwner: TComponent); override;
|
|
|
|
+ destructor Destroy; override;
|
|
end;
|
|
end;
|
|
|
|
|
|
- TCustomTCPServer = class;
|
|
|
|
|
|
|
|
- TQueryConnectEvent = procedure(Sender: TCustomTCPServer; Socket: Integer;
|
|
|
|
|
|
+ TQueryConnectEvent = procedure(Sender: TConnectionBasedSocket; Socket: Integer;
|
|
var DoConnect: Boolean) of object;
|
|
var DoConnect: Boolean) of object;
|
|
- TConnectEvent = procedure(Sender: TCustomTCPServer;
|
|
|
|
|
|
+ TConnectEvent = procedure(Sender: TConnectionBasedSocket;
|
|
Stream: TSocketStream) of object;
|
|
Stream: TSocketStream) of object;
|
|
|
|
|
|
- TCustomTCPServer = class(TTCPConnection)
|
|
|
|
|
|
+ TSocketConnectionServer = class(TConnectionBasedSocket)
|
|
private
|
|
private
|
|
- FActive: Boolean;
|
|
|
|
- FPort: Word;
|
|
|
|
FOnQueryConnect: TQueryConnectEvent;
|
|
FOnQueryConnect: TQueryConnectEvent;
|
|
FOnConnect: TConnectEvent;
|
|
FOnConnect: TConnectEvent;
|
|
- procedure SetActive(Value: Boolean);
|
|
|
|
- procedure ListenerDataAvailable(Sender: TObject);
|
|
|
|
protected
|
|
protected
|
|
- FSocket: Integer;
|
|
|
|
-
|
|
|
|
- function DoQueryConnect(ASocket: Integer): Boolean; virtual;
|
|
|
|
|
|
+ DataAvailableNotifyHandle: Pointer;
|
|
|
|
+ procedure ListenerDataAvailable(Sender: TObject);
|
|
|
|
+ function DoQueryConnect(ASocket: Integer): Boolean;
|
|
procedure DoConnect(AStream: TSocketStream); 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
|
|
property OnQueryConnect: TQueryConnectEvent read FOnQueryConnect
|
|
write FOnQueryConnect;
|
|
write FOnQueryConnect;
|
|
property OnConnect: TConnectEvent read FOnConnect write FOnConnect;
|
|
property OnConnect: TConnectEvent read FOnConnect write FOnConnect;
|
|
end;
|
|
end;
|
|
|
|
|
|
- TTCPServer = class(TCustomTCPServer)
|
|
|
|
|
|
+
|
|
|
|
+ // TCP/IP components
|
|
|
|
+
|
|
|
|
+ TCustomTCPClient = class(TClientConnectionSocket)
|
|
|
|
+ private
|
|
|
|
+ FHost: String;
|
|
|
|
+ FPort: Word;
|
|
|
|
+ HostAddr: THostAddr;
|
|
|
|
+ procedure SetHost(const Value: String);
|
|
|
|
+ procedure SetPort(Value: Word);
|
|
|
|
+ protected
|
|
|
|
+ procedure CreateSocket; override;
|
|
|
|
+ procedure DoResolve; override;
|
|
|
|
+ procedure DoConnect; override;
|
|
|
|
+ function GetPeerName: String; override;
|
|
|
|
+
|
|
|
|
+ property Host: String read FHost write SetHost;
|
|
|
|
+ property Port: Word read FPort write SetPort;
|
|
public
|
|
public
|
|
- property Socket: Integer read FSocket;
|
|
|
|
|
|
+ destructor Destroy; override;
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+ TTCPClient = class(TCustomTCPClient)
|
|
|
|
+ public
|
|
|
|
+ property ConnectionState;
|
|
|
|
+ property Stream;
|
|
published
|
|
published
|
|
property Active;
|
|
property Active;
|
|
|
|
+ property Host;
|
|
property Port;
|
|
property Port;
|
|
- property OnQueryConnect;
|
|
|
|
- property OnConnect;
|
|
|
|
|
|
+ property Retries;
|
|
|
|
+ property RetryDelay;
|
|
|
|
+ property OnConnectionStateChange;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
+ TCustomTCPServer = class;
|
|
|
|
|
|
- // UDP/IP components
|
|
|
|
-
|
|
|
|
- TUDPBase = class(TSocketComponent)
|
|
|
|
- end;
|
|
|
|
-
|
|
|
|
- TUDPClient = class(TUDPBase)
|
|
|
|
|
|
+ TCustomTCPServer = class(TSocketConnectionServer)
|
|
|
|
+ private
|
|
|
|
+ FPort: Word;
|
|
|
|
+ procedure SetActive(Value: Boolean); override;
|
|
|
|
+ protected
|
|
|
|
+ //!!!: Interface/bindings list?
|
|
|
|
+ property Port: Word read FPort write FPort;
|
|
|
|
+ public
|
|
|
|
+ destructor Destroy; override;
|
|
end;
|
|
end;
|
|
|
|
|
|
- TUDPServer = class(TUDPBase)
|
|
|
|
|
|
+ TTCPServer = class(TCustomTCPServer)
|
|
|
|
+ public
|
|
|
|
+ property Stream;
|
|
|
|
+ published
|
|
|
|
+ property Active;
|
|
|
|
+ property Port;
|
|
|
|
+ property OnQueryConnect;
|
|
|
|
+ property OnConnect;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
|
|
implementation
|
|
implementation
|
|
|
|
|
|
-uses Resolve;
|
|
|
|
|
|
+uses
|
|
|
|
+{$IFDEF VER1_0}
|
|
|
|
+ Linux;
|
|
|
|
+{$ELSE}
|
|
|
|
+ Unix;
|
|
|
|
+{$ENDIF}
|
|
|
|
|
|
resourcestring
|
|
resourcestring
|
|
- SSocketCreationError = 'Could not create socket';
|
|
|
|
- SSocketBindingError = 'Could not bind socket to port %d';
|
|
|
|
- SSocketAcceptError = 'Connection accept failed';
|
|
|
|
|
|
+ SSocketNoEventLoopAssigned = 'No event loop assigned';
|
|
|
|
+ SSocketCreationError = 'Could not create socket: %s';
|
|
|
|
+ SHostNotFound = 'Host "%s" not found';
|
|
|
|
+ SSocketConnectFailed = 'Could not connect to %s: %s';
|
|
|
|
+ SSocketBindingError = 'Could not bind socket to port %d: %s';
|
|
|
|
+ SSocketAcceptError = 'Connection accept failed: %s';
|
|
|
|
+ SSocketIsActive = 'Cannot change parameters while active';
|
|
|
|
+
|
|
|
|
|
|
-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;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+// TSocketStream
|
|
|
|
+
|
|
destructor TSocketStream.Destroy;
|
|
destructor TSocketStream.Destroy;
|
|
begin
|
|
begin
|
|
FileClose(Handle);
|
|
FileClose(Handle);
|
|
@@ -142,8 +219,8 @@ begin
|
|
if Result = -1 then
|
|
if Result = -1 then
|
|
begin
|
|
begin
|
|
Result := 0;
|
|
Result := 0;
|
|
- if Assigned(OnDisconnect) then
|
|
|
|
- OnDisconnect(Self);
|
|
|
|
|
|
+ if SocketError <> Sys_EAGAIN then
|
|
|
|
+ Disconnected;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
@@ -153,11 +230,17 @@ begin
|
|
if Result = -1 then
|
|
if Result = -1 then
|
|
begin
|
|
begin
|
|
Result := 0;
|
|
Result := 0;
|
|
- if Assigned(OnDisconnect) then
|
|
|
|
- OnDisconnect(Self);
|
|
|
|
|
|
+ if SocketError <> Sys_EAGAIN then
|
|
|
|
+ Disconnected;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
+procedure TSocketStream.Disconnected;
|
|
|
|
+begin
|
|
|
|
+ if Assigned(OnDisconnect) then
|
|
|
|
+ OnDisconnect(Self);
|
|
|
|
+end;
|
|
|
|
+
|
|
function TSocketStream.GetLocalAddress: TSockAddr;
|
|
function TSocketStream.GetLocalAddress: TSockAddr;
|
|
var
|
|
var
|
|
len: LongInt;
|
|
len: LongInt;
|
|
@@ -177,21 +260,289 @@ begin
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
|
|
-function TCustomTCPServer.DoQueryConnect(ASocket: Integer): Boolean;
|
|
|
|
|
|
+// TConnectionBasedSocket
|
|
|
|
+
|
|
|
|
+destructor TConnectionBasedSocket.Destroy;
|
|
|
|
+begin
|
|
|
|
+ FreeAndNil(FStream);
|
|
|
|
+ inherited Destroy;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+// TClientConnectionSocket
|
|
|
|
+
|
|
|
|
+constructor TClientConnectionSocket.Create(AOwner: TComponent);
|
|
|
|
+begin
|
|
|
|
+ inherited Create(AOwner);
|
|
|
|
+ FRetryDelay := 500;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+destructor TClientConnectionSocket.Destroy;
|
|
|
|
+begin
|
|
|
|
+ if Assigned(RetryTimerNotifyHandle) then
|
|
|
|
+ EventLoop.RemoveTimerNotify(RetryTimerNotifyHandle);
|
|
|
|
+ inherited Destroy;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TClientConnectionSocket.DoResolve;
|
|
|
|
+begin
|
|
|
|
+ // By default, no resolving is done, so continue directly with connecting
|
|
|
|
+ DoConnect;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TClientConnectionSocket.DoConnect;
|
|
|
|
+begin
|
|
|
|
+ SetConnectionState(connConnecting);
|
|
|
|
+
|
|
|
|
+ try
|
|
|
|
+ if not Assigned(EventLoop) then
|
|
|
|
+ raise ESocketError.Create(SSocketNoEventLoopAssigned);
|
|
|
|
+ CanWriteNotifyHandle := EventLoop.SetCanWriteNotify(Stream.Handle,
|
|
|
|
+ @SocketCanWrite, nil);
|
|
|
|
+ except
|
|
|
|
+ SetConnectionState(connDisconnected);
|
|
|
|
+ raise;
|
|
|
|
+ end;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TClientConnectionSocket.SetActive(Value: Boolean);
|
|
|
|
+begin
|
|
|
|
+ if Value <> Active then
|
|
|
|
+ begin
|
|
|
|
+ if Value then
|
|
|
|
+ begin
|
|
|
|
+ // Activate the connection
|
|
|
|
+ FActive := True;
|
|
|
|
+ RetryCounter := 0;
|
|
|
|
+
|
|
|
|
+ CreateSocket;
|
|
|
|
+ DoResolve;
|
|
|
|
+ end else
|
|
|
|
+ begin
|
|
|
|
+ // Close the connection
|
|
|
|
+ FActive := False;
|
|
|
|
+ try
|
|
|
|
+ FreeAndNil(FStream);
|
|
|
|
+ if Assigned(CanWriteNotifyHandle) then
|
|
|
|
+ begin
|
|
|
|
+ EventLoop.ClearCanWriteNotify(CanWriteNotifyHandle);
|
|
|
|
+ CanWriteNotifyHandle := nil;
|
|
|
|
+ end;
|
|
|
|
+ if Assigned(RetryTimerNotifyHandle) then
|
|
|
|
+ begin
|
|
|
|
+ EventLoop.RemoveTimerNotify(RetryTimerNotifyHandle);
|
|
|
|
+ RetryTimerNotifyHandle := nil;
|
|
|
|
+ end;
|
|
|
|
+ finally
|
|
|
|
+ SetConnectionState(connDisconnected);
|
|
|
|
+ end;
|
|
|
|
+ end;
|
|
|
|
+ end;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TClientConnectionSocket.SetConnectionState(NewState:
|
|
|
|
+ TConnectionState);
|
|
|
|
+var
|
|
|
|
+ OldState: TConnectionState;
|
|
|
|
+begin
|
|
|
|
+ if NewState <> ConnectionState then
|
|
|
|
+ begin
|
|
|
|
+ OldState := ConnectionState;
|
|
|
|
+ FConnectionState := NewState;
|
|
|
|
+ if Assigned(OnConnectionStateChange) then
|
|
|
|
+ OnConnectionStateChange(Self, OldState, NewState);
|
|
|
|
+ end;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+procedure TClientConnectionSocket.RetryTimerNotify(Sender: TObject);
|
|
|
|
+begin
|
|
|
|
+ RetryTimerNotifyHandle := nil;
|
|
|
|
+ Active := True;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TClientConnectionSocket.SocketCanWrite(Sender: TObject);
|
|
|
|
+var
|
|
|
|
+ Error: Integer;
|
|
|
|
+ ErrorLen, GetResult: LongInt;
|
|
|
|
+begin
|
|
|
|
+ if ConnectionState = connConnecting then
|
|
|
|
+ begin
|
|
|
|
+ EventLoop.ClearCanWriteNotify(CanWriteNotifyHandle);
|
|
|
|
+ CanWriteNotifyHandle := nil;
|
|
|
|
+
|
|
|
|
+ ErrorLen := SizeOf(Error);
|
|
|
|
+ GetResult := Sockets.GetSocketOptions(Stream.Handle, SOL_SOCKET, SO_ERROR,
|
|
|
|
+ Error, ErrorLen);
|
|
|
|
+ if GetResult <> 0 then
|
|
|
|
+ raise ESocketError.CreateFmt(SSocketConnectFailed,
|
|
|
|
+ [GetPeerName, StrError(GetResult)]);
|
|
|
|
+ if Error <> 0 then
|
|
|
|
+ if (RetryCounter >= Retries) and (Retries >= 0) then
|
|
|
|
+ raise ESocketError.CreateFmt(SSocketConnectFailed,
|
|
|
|
+ [GetPeerName, StrError(Error)])
|
|
|
|
+ else begin
|
|
|
|
+ Active := False;
|
|
|
|
+ RetryTimerNotifyHandle := EventLoop.AddTimerNotify(RetryDelay, False,
|
|
|
|
+ @RetryTimerNotify, Self);
|
|
|
|
+ Inc(RetryCounter);
|
|
|
|
+ end
|
|
|
|
+ else
|
|
|
|
+ begin
|
|
|
|
+ RetryCounter := 0;
|
|
|
|
+ SetConnectionState(connConnected);
|
|
|
|
+ end;
|
|
|
|
+ end;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+// TSocketConnectionServer
|
|
|
|
+
|
|
|
|
+procedure TSocketConnectionServer.ListenerDataAvailable(Sender: TObject);
|
|
|
|
+var
|
|
|
|
+ ClientSocket: Integer;
|
|
|
|
+ Addr: TInetSockAddr;
|
|
|
|
+ AddrSize: Integer;
|
|
|
|
+begin
|
|
|
|
+ AddrSize := SizeOf(Addr);
|
|
|
|
+ ClientSocket := Accept(Stream.Handle, Addr, AddrSize);
|
|
|
|
+ if ClientSocket = -1 then
|
|
|
|
+ raise ESocketError.CreateFmt(SSocketAcceptError, [StrError(SocketError)]);
|
|
|
|
+
|
|
|
|
+ if DoQueryConnect(ClientSocket) then
|
|
|
|
+ DoConnect(TSocketStream.Create(ClientSocket));
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+function TSocketConnectionServer.DoQueryConnect(ASocket: Integer): Boolean;
|
|
begin
|
|
begin
|
|
Result := True;
|
|
Result := True;
|
|
if Assigned(OnQueryConnect) then
|
|
if Assigned(OnQueryConnect) then
|
|
OnQueryConnect(Self, ASocket, Result);
|
|
OnQueryConnect(Self, ASocket, Result);
|
|
end;
|
|
end;
|
|
|
|
|
|
-procedure TCustomTCPServer.DoConnect(AStream: TSocketStream);
|
|
|
|
|
|
+procedure TSocketConnectionServer.DoConnect(AStream: TSocketStream);
|
|
begin
|
|
begin
|
|
if Assigned(OnConnect) then
|
|
if Assigned(OnConnect) then
|
|
OnConnect(Self, AStream);
|
|
OnConnect(Self, AStream);
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
+
|
|
|
|
+// TCustomTCPClient
|
|
|
|
+
|
|
|
|
+type
|
|
|
|
+ TClientSocketStream = class(TSocketStream)
|
|
|
|
+ protected
|
|
|
|
+ Client: TCustomTCPClient;
|
|
|
|
+ procedure Disconnected; override;
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+procedure TClientSocketStream.Disconnected;
|
|
|
|
+begin
|
|
|
|
+ inherited Disconnected;
|
|
|
|
+ Client.Active := False;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+destructor TCustomTCPClient.Destroy;
|
|
|
|
+begin
|
|
|
|
+ if Assigned(CanWriteNotifyHandle) then
|
|
|
|
+ begin
|
|
|
|
+ EventLoop.ClearCanWriteNotify(CanWriteNotifyHandle);
|
|
|
|
+ // Set to nil to be sure that descendant classes don't do something stupid
|
|
|
|
+ CanWriteNotifyHandle := nil;
|
|
|
|
+ end;
|
|
|
|
+ inherited Destroy;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TCustomTCPClient.SetHost(const Value: String);
|
|
|
|
+begin
|
|
|
|
+ if Value <> Host then
|
|
|
|
+ begin
|
|
|
|
+ if Active then
|
|
|
|
+ raise ESocketError.Create(SSocketIsActive);
|
|
|
|
+ FHost := Value;
|
|
|
|
+ end;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TCustomTCPClient.SetPort(Value: Word);
|
|
|
|
+begin
|
|
|
|
+ if Value <> Port then
|
|
|
|
+ begin
|
|
|
|
+ if Active then
|
|
|
|
+ raise ESocketError.Create(SSocketIsActive);
|
|
|
|
+ FPort := Value;
|
|
|
|
+ end;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TCustomTCPClient.DoResolve;
|
|
|
|
+var
|
|
|
|
+ HostResolver: THostResolver;
|
|
|
|
+begin
|
|
|
|
+ HostAddr := StrToHostAddr(Host);
|
|
|
|
+ if HostAddr[1] = 0 then
|
|
|
|
+ begin
|
|
|
|
+ HostResolver := THostResolver.Create(nil);
|
|
|
|
+ try
|
|
|
|
+ SetConnectionState(connResolving);
|
|
|
|
+ if not HostResolver.NameLookup(FHost) then
|
|
|
|
+ raise ESocketError.CreateFmt(SHostNotFound, [Host]);
|
|
|
|
+ HostAddr := HostResolver.HostAddress;
|
|
|
|
+ finally
|
|
|
|
+ HostResolver.Free;
|
|
|
|
+ end;
|
|
|
|
+ end;
|
|
|
|
+ DoConnect;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TCustomTCPClient.CreateSocket;
|
|
|
|
+var
|
|
|
|
+ Socket: Integer;
|
|
|
|
+begin
|
|
|
|
+
|
|
|
|
+ Socket := Sockets.Socket(AF_INET, SOCK_STREAM, 0);
|
|
|
|
+ if Socket = -1 then
|
|
|
|
+ raise ESocketError.CreateFmt(SSocketCreationError,
|
|
|
|
+ [StrError(SocketError)]);
|
|
|
|
+ FStream := TClientSocketStream.Create(Socket);
|
|
|
|
+ TClientSocketStream(FStream).Client := Self;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TCustomTCPClient.DoConnect;
|
|
|
|
+var
|
|
|
|
+ SockAddr: TInetSockAddr;
|
|
|
|
+begin
|
|
|
|
+ inherited DoConnect;
|
|
|
|
+ SockAddr.Family := AF_INET;
|
|
|
|
+ SockAddr.Port := ShortHostToNet(Port);
|
|
|
|
+ SockAddr.Addr := Cardinal(HostAddr);
|
|
|
|
+ Sockets.Connect(Stream.Handle, SockAddr, SizeOf(SockAddr));
|
|
|
|
+ if (SocketError <> sys_EINPROGRESS) and (SocketError <> 0) then
|
|
|
|
+ raise ESocketError.CreateFmt(SSocketConnectFailed,
|
|
|
|
+ [GetPeerName, StrError(SocketError)]);
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+function TCustomTCPClient.GetPeerName: String;
|
|
|
|
+begin
|
|
|
|
+ Result := Format('%s:%d', [Host, Port]);
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+// TCustomTCPServer
|
|
|
|
+
|
|
|
|
+destructor TCustomTCPServer.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;
|
|
|
|
+
|
|
procedure TCustomTCPServer.SetActive(Value: Boolean);
|
|
procedure TCustomTCPServer.SetActive(Value: Boolean);
|
|
var
|
|
var
|
|
|
|
+ Socket: Integer;
|
|
Addr: TInetSockAddr;
|
|
Addr: TInetSockAddr;
|
|
begin
|
|
begin
|
|
if Active <> Value then
|
|
if Active <> Value then
|
|
@@ -199,47 +550,42 @@ begin
|
|
FActive := False;
|
|
FActive := False;
|
|
if Value then
|
|
if Value then
|
|
begin
|
|
begin
|
|
- FSocket := Sockets.Socket(AF_INET, SOCK_STREAM, 0);
|
|
|
|
- if FSocket = -1 then
|
|
|
|
- raise ESocketError.Create(SSocketCreationError);
|
|
|
|
|
|
+ Socket := Sockets.Socket(AF_INET, SOCK_STREAM, 0);
|
|
|
|
+ if Socket = -1 then
|
|
|
|
+ raise ESocketError.CreateFmt(SSocketCreationError,
|
|
|
|
+ [StrError(SocketError)]);
|
|
|
|
+ FStream := TSocketStream.Create(Socket);
|
|
Addr.Family := AF_INET;
|
|
Addr.Family := AF_INET;
|
|
Addr.Port := ShortHostToNet(Port);
|
|
Addr.Port := ShortHostToNet(Port);
|
|
Addr.Addr := 0;
|
|
Addr.Addr := 0;
|
|
- if not Bind(FSocket, Addr, SizeOf(Addr)) then
|
|
|
|
- raise ESocketError.CreateFmt(SSocketBindingError, [Port]);
|
|
|
|
- Listen(FSocket, 5);
|
|
|
|
- DataAvailableNotifyHandle := EventLoop.SetDataAvailableNotify(FSocket,
|
|
|
|
|
|
+ if not Bind(Socket, Addr, SizeOf(Addr)) then
|
|
|
|
+ raise ESocketError.CreateFmt(SSocketBindingError,
|
|
|
|
+ [Port, StrError(SocketError)]);
|
|
|
|
+ Listen(Socket, 5);
|
|
|
|
+ if not Assigned(EventLoop) then
|
|
|
|
+ raise ESocketError.Create(SSocketNoEventLoopAssigned);
|
|
|
|
+ DataAvailableNotifyHandle := EventLoop.SetDataAvailableNotify(Socket,
|
|
@ListenerDataAvailable, nil);
|
|
@ListenerDataAvailable, nil);
|
|
|
|
+ FActive := True;
|
|
end else
|
|
end else
|
|
begin
|
|
begin
|
|
- FileClose(FSocket);
|
|
|
|
|
|
+ FreeAndNil(FStream);
|
|
EventLoop.ClearDataAvailableNotify(DataAvailableNotifyHandle);
|
|
EventLoop.ClearDataAvailableNotify(DataAvailableNotifyHandle);
|
|
DataAvailableNotifyHandle := nil;
|
|
DataAvailableNotifyHandle := nil;
|
|
end;
|
|
end;
|
|
end;
|
|
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.
|
|
end.
|
|
|
|
|
|
|
|
|
|
{
|
|
{
|
|
$Log$
|
|
$Log$
|
|
- Revision 1.1 2003-11-22 11:55:28 sg
|
|
|
|
|
|
+ Revision 1.2 2004-01-31 19:13:14 sg
|
|
|
|
+ * Splittet old HTTP unit into httpbase and httpclient
|
|
|
|
+ * Many improvements in fpSock (e.g. better disconnection detection)
|
|
|
|
+
|
|
|
|
+ Revision 1.1 2003/11/22 11:55:28 sg
|
|
* First version, a simple starting point for further development
|
|
* First version, a simple starting point for further development
|
|
|
|
|
|
}
|
|
}
|