📄 wsockets.pas
字号:
unit WSocketS;
interface
uses
WinTypes, WinProcs, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs, WSocket, Winsock;
const
WSocketServerVersion = 106;
CopyRight : String = ' TWSocketServer (c) 1999-2002 F. Piette V1.06 ';
WM_CLIENT_CLOSED = WM_USER + 30;
DefaultBanner = 'Welcome to TcpSrv';
type
TCustomWSocketServer = class;
TWSocketClient = class;
TWSocketClientClass = class of TWSocketClient;
TWSocketClientCreateEvent = procedure (Sender : TObject;
Client : TWSocketClient) of object;
TWSocketClientConnectEvent = procedure (Sender : TObject;
Client : TWSocketClient;
Error : Word) of object;
{ TWSocketClient is used to handle all client connections. }
{ Altough you may use it directly, you'll probably wants to use your }
{ own derived component to add data and methods suited to your }
{ application. }
{ If you use a derived component, then assign it's class to }
{ TWSocketServer ClientClass property. }
TWSocketClient = class(TWSocket)
protected
FBanner : String;
FServer : TCustomWSocketServer;
FPeerAddr : String;
FPeerPort : String;
FSessionClosedFlag : Boolean;
public
procedure StartConnection; virtual;
procedure TriggerSessionClosed(Error : Word); override;
procedure Dup(newHSocket : TSocket); override;
function GetPeerAddr: String; override;
function GetPeerPort: String; override;
property Server : TCustomWSocketServer read FServer
write FServer;
published
property Banner : String read FBanner
write FBanner;
end;
{ TWSocketServer is made for listening for tcp client connections. }
{ For each connection, it instanciate a new TWSocketClient (or derived) }
{ to handle connection. Use ClientClass to specify your derived. }
TCustomWSocketServer = class(TWSocket)
protected
FBanner : String;
FBannerToBusy : String;
FClientClass : TWSocketClientClass;
FClientList : TList;
FClientNum : LongInt;
FMaxClients : LongInt;
FOnClientCreate : TWSocketClientCreateEvent;
FOnClientConnect : TWSocketClientConnectEvent;
FOnClientDisconnect : TWSocketClientConnectEvent;
procedure WndProc(var MsgRec: TMessage); override;
procedure Notification(AComponent: TComponent; operation: TOperation); override;
procedure TriggerSessionAvailable(Error : Word); override;
procedure TriggerClientCreate(Client : TWSocketClient); virtual;
procedure TriggerClientConnect(Client : TWSocketClient; Error : Word); virtual;
procedure TriggerClientDisconnect(Client : TWSocketClient; Error : Word); virtual;
function GetClientCount : Integer; virtual;
function GetClient(nIndex : Integer) : TWSocketClient; virtual;
procedure WMClientClosed(var msg: TMessage);
message WM_CLIENT_CLOSED;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
{ Check if a given object is one of our clients }
function IsClient(SomeThing : TObject) : Boolean;
protected
{ TWSocketClient derived class to instanciate for each client }
property ClientClass : TWSocketClientClass
read FClientClass
write FClientClass;
{ How many active clients we currently have }
property ClientCount : Integer read GetClientCount;
{ Client[] give direct access to anyone of our clients }
property Client[nIndex : Integer] : TWSocketClient
read GetClient;
published
{ Banner sent to client as welcome message. Can be empty. }
property Banner : String read FBanner
write FBanner;
property BannerToBusy : String read FBannerToBusy
write FBannerToBusy;
property MaxClients : LongInt read FMaxClients
write FMaxClients;
{ Triggered when a client disconnect }
property OnClientDisconnect : TWSocketClientConnectEvent
read FOnClientDisconnect
write FOnClientDisconnect;
{ Triggerred when a new client is connecting }
property OnClientConnect : TWSocketClientConnectEvent
read FOnClientConnect
write FOnClientConnect;
{ Triggerred when a new client component has been created }
property OnClientCreate : TWSocketClientCreateEvent
read FOnClientCreate
write FOnClientCreate;
end;
TWSocketServer = class(TCustomWSocketServer)
public
property ClientClass;
property ClientCount;
property Client;
published
property Banner;
property BannerToBusy;
property MaxClients;
property OnClientDisconnect;
property OnClientConnect;
end;
procedure Register;
implementation
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
procedure Register;
begin
RegisterComponents('FPiette', [TWSocketServer]);
end;
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
constructor TCustomWSocketServer.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FClientList := TList.Create;
FClientClass := TWSocketClient;
FBanner := DefaultBanner;
FBannerToBusy := 'Sorry, too many clients';
end;
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
destructor TCustomWSocketServer.Destroy;
var
I : Integer;
begin
if Assigned(FClientList) then begin
{ We need to destroy all clients }
for I := FClientList.Count - 1 downto 0 do begin
try
TWSocketClient(FClientList.Items[I]).Destroy;
except
{ Ignore any exception here }
end;
end;
{ Then we can destroy client list }
FClientList.Free;
FClientList := nil;
end;
{ And finally destroy ourself }
inherited Destroy;
end;
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
{ Message handler }
procedure TCustomWSocketServer.WndProc(var MsgRec: TMessage);
begin
with MsgRec do begin
if Msg = WM_CLIENT_CLOSED then begin
{ We *MUST* handle all exception to avoid application shutdown }
try
WMClientClosed(MsgRec)
except
on E:Exception do
HandleBackGroundException(E);
end;
end
else
inherited WndProc(MsgRec);
end;
end;
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
{ Called by destructor when child component (a clients) is create or }
{ destroyed. }
procedure TCustomWSocketServer.Notification(
AComponent : TComponent;
Operation : TOperation);
begin
inherited Notification(AComponent, Operation);
if Assigned(FClientList) and (AComponent is TWSocketClient) then begin
if Operation = opInsert then
{ A new client has been created, add it to our list }
FClientList.Add(AComponent)
else if Operation = opRemove then
{ If one of our client has been destroyed, remove it from our list }
FClientList.Remove(AComponent);
end;
end;
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
{ Called when a session is available, that is when a client is connecting }
procedure TCustomWSocketServer.TriggerSessionAvailable(Error : Word);
var
Client : TWSocketClient;
begin
{ Call parent event handler }
inherited TriggerSessionAvailable(Error);
{ In case of error, do nothing }
if Error <> 0 then
Exit;
Inc(FClientNum);
Client := FClientClass.Create(Self);
TriggerClientCreate(Client);
Client.Name := Name + 'Client' + IntToStr(FClientNum);
Client.Banner := FBanner;
Client.Server := Self;
Client.HSocket := Accept;
TriggerClientConnect(Client, Error);
{ The event handler may have destroyed the client ! }
if FClientList.IndexOf(Client) < 0 then
Exit;
{ The event handler may have closed the connection }
if Client.State <> wsConnected then
Exit;
{ Ok, the client is still there, process with the connection }
if (FMaxClients > 0) and (FMaxClients < ClientCount) then begin
{ Sorry, toomuch clients }
Client.Banner := FBannerToBusy;
Client.StartConnection;
Client.Close;
end
else
Client.StartConnection;
end;
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
procedure TCustomWSocketServer.TriggerClientConnect(
Client : TWSocketClient; Error : Word);
begin
if Assigned(FOnClientConnect) then
FOnClientConnect(Self, Client, Error);
end;
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
procedure TCustomWSocketServer.TriggerClientCreate(Client : TWSocketClient);
begin
if Assigned(FOnClientCreate) then
FOnClientCreate(Self, Client);
end;
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
procedure TCustomWSocketServer.TriggerClientDisconnect(
Client : TWSocketClient; Error : Word);
begin
if Assigned(FOnClientDisconnect) then
FOnClientDisconnect(Self, Client, Error);
end;
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
{ get number of connect clients }
function TCustomWSocketServer.GetClientCount : Integer;
begin
if Assigned(FClientList) then
Result := FClientList.Count
else
Result := 0;
end;
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
{ Acces method to return a client by index. }
{ Return nil if index is out of range. }
function TCustomWSocketServer.GetClient(nIndex : Integer) : TWSocketClient;
begin
if not Assigned(FClientList) then begin
Result := nil;
Exit;
end;
if (nIndex < 0) or (nIndex >= FClientList.Count) then begin
Result := nil;
Exit;
end;
Result := TWSocketClient(FClientList.Items[nIndex]);
end;
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
{ Client has closed. Remove it from client list and destroy component. }
procedure TCustomWSocketServer.WMClientClosed(var msg: TMessage);
var
Client : TWSocketClient;
begin
Client := TWSocketClient(Msg.LParam);
try
TriggerClientDisconnect(Client, Error);
finally
{ Calling Destroy will automatically remove client from list because }
{ we installed a notification handler. }
Client.Destroy;
end;
end;
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
{ Check if a given object is one of our clients. }
function TCustomWSocketServer.IsClient(SomeThing : TObject) : Boolean;
begin
if not Assigned(FClientList) then
Result := FALSE
else
Result := (FClientList.IndexOf(Pointer(SomeThing)) >= 0);
end;
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
{* *}
{* TWSocketClient *}
{* *}
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
procedure TWSocketClient.StartConnection;
begin
if Length(FBanner) > 0 then
SendStr(FBanner + FLineEnd);
end;
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
{ Triggered when socket is closed. Need to inform server socket to update }
{ client list and trigger client disconnect event. }
procedure TWSocketClient.TriggerSessionClosed(Error : Word);
begin
if not FSessionClosedFlag then begin
FSessionClosedFlag := TRUE;
if Assigned(Server) then
PostMessage(Server.Handle, WM_CLIENT_CLOSED, 0, LongInt(Self));
inherited TriggerSessionClosed(Error);
end;
end;
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
{ This override base class GetPeerAddr. It return cached value. }
function TWSocketClient.GetPeerAddr: String;
begin
Result := FPeerAddr;
end;
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
{ This override base class GetPeerPort. It return cached value. }
function TWSocketClient.GetPeerPort: String;
begin
Result := FPeerPort;
end;
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
{ Override base class. Dup is called when a client is assigned to a }
{ TWSocket. Assigning HSocket property will call Dup. }
procedure TWSocketClient.Dup(newHSocket : TSocket);
begin
inherited Dup(newHSocket);
{ Cache PeerAddr value }
FPeerAddr := inherited GetPeerAddr;
FPeerPort := inherited GetPeerPort;
end;
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
end.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -