我所改造的JSocket适用于任何DELPHI版本?

JSOCKET是异步选择模式的通信控件,简单而强大,传奇的早期版本就是使用它作通信。

{ *********************************************************************** }

{ }

{ Delphi Runtime Library }

{ }

{ Copyright (c) 1997-2001 Borland Software Corporation }

{ }

{ *********************************************************************** }

{*******************************************************}

{ Windows socket components }

{*******************************************************}

unit JSocket;

interface

uses SysUtils, Windows, Messages, Classes, WinSock, SyncObjs;

const

CM_SOCKETMESSAGE = WM_USER + $0001;

CM_DEFERFREE = WM_USER + $0002;

CM_LOOKUPCOMPLETE = WM_USER + $0003;

type

ESocketError = class(Exception);

TCMSocketMessage = record

Msg: Cardinal;

Socket: TSocket;

SelectEvent: Word;

SelectError: Word;

Result: Longint;

end;

TCMLookupComplete = record

Msg: Cardinal;

LookupHandle: THandle;

AsyncBufLen: Word;

AsyncError: Word;

Result: Longint;

end;

TCustomWinSocket = class;

TCustomSocket = class;

TServerAcceptThread = class;

TServerClientThread = class;

TServerWinSocket = class;

TServerClientWinSocket = class;

TServerType = (stNonBlocking, stThreadBlocking);

TClientType = (ctNonBlocking, ctBlocking);

TAsyncStyle = (asRead, asWrite, asOOB, asAccept, asConnect, asClose);

TAsyncStyles = set of TAsyncStyle;

TSocketEvent = (seLookup, seConnecting, seConnect, seDisconnect, seListen,

seAccept, seWrite, seRead);

TLookupState = (lsIdle, lsLookupAddress, lsLookupService);

TErrorEvent = (eeGeneral, eeSend, eeReceive, eeConnect, eeDisconnect, eeAccept, eeLookup);

TSocketEventEvent = procedure (Sender: TObject; Socket: TCustomWinSocket;

SocketEvent: TSocketEvent) of object;

TSocketErrorEvent = procedure (Sender: TObject; Socket: TCustomWinSocket;

ErrorEvent: TErrorEvent; var ErrorCode: Integer) of object;

TGetSocketEvent = procedure (Sender: TObject; Socket: TSocket;

var ClientSocket: TServerClientWinSocket) of object;

TGetThreadEvent = procedure (Sender: TObject; ClientSocket: TServerClientWinSocket;

var SocketThread: TServerClientThread) of object;

TSocketNotifyEvent = procedure (Sender: TObject; Socket: TCustomWinSocket) of object;

TCustomWinSocket = class

private

FSocket: TSocket;

FConnected: Boolean;

FSendStream: TStream;

FDropAfterSend: Boolean;

FHandle: HWnd;

FAddr: TSockAddrIn;

FAsyncStyles: TASyncStyles;

FLookupState: TLookupState;

FLookupHandle: THandle;

FOnSocketEvent: TSocketEventEvent;

FOnErrorEvent: TSocketErrorEvent;

FSocketLock: TCriticalSection;

FGetHostData: Pointer;

FData: Pointer;

// Used during non-blocking host and service lookups

FService: AnsiString;

FPort: Word;

FClient: Boolean;

FQueueSize: Integer;

function SendStreamPiece: Boolean;

procedure WndProc(var Message: TMessage);

procedure CMLookupComplete(var Message: TCMLookupComplete); message CM_LOOKUPCOMPLETE;

procedure CMSocketMessage(var Message: TCMSocketMessage); message CM_SOCKETMESSAGE;

procedure CMDeferFree(var Message); message CM_DEFERFREE;

procedure DeferFree;

procedure DoSetAsyncStyles;

function GetHandle: HWnd;

function GetLocalHost: AnsiString;

function GetLocalAddress: AnsiString;

function GetLocalPort: Integer;

function GetRemoteHost: AnsiString;

function GetRemoteAddress: AnsiString;

function GetRemotePort: Integer;

function GetRemoteAddr: TSockAddrIn;

function CheckSocketResult(ResultCode: Integer;

const Op: AnsiString): Integer;

protected

procedure AsyncInitSocket(const Name, Address, Service: AnsiString; Port: Word;

QueueSize: Integer; Client: Boolean);

procedure DoOpen;

procedure DoListen(QueueSize: Integer);

function InitSocket(const Name, Address, Service: AnsiString; Port: Word;

Client: Boolean): TSockAddrIn;

procedure Event(Socket: TCustomWinSocket; SocketEvent: TSocketEvent); dynamic;

procedure Error(Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;

var ErrorCode: Integer); dynamic;

procedure SetAsyncStyles(Value: TASyncStyles);

public

nIndex:Integer;

constructor Create(ASocket: TSocket);

destructor Destroy; override;

procedure Close;

procedure DefaultHandler(var Message); override;

procedure Lock;

procedure Unlock;

procedure Listen(const Name, Address, Service: AnsiString; Port: Word;

QueueSize: Integer; Block: Boolean = True);

procedure Open(const Name, Address, Service: AnsiString; Port: Word; Block: Boolean = True);

procedure Accept(Socket: TSocket); virtual;

procedure Connect(Socket: TSocket); virtual;

procedure Disconnect(Socket: TSocket); virtual;

procedure Read(Socket: TSocket); virtual;

procedure Write(Socket: TSocket); virtual;

function LookupName(const name: AnsiString): TInAddr;

function LookupService(const service: AnsiString): Integer;

function ReceiveLength: Integer;

function ReceiveBuf(var Buf; Count: Integer): Integer;

function ReceiveText: AnsiString;

function SendBuf(var Buf; Count: Integer): Integer;

function SendStream(AStream: TStream): Boolean;

function SendStreamThenDrop(AStream: TStream): Boolean;

function SendText(const S: AnsiString): Integer;

property LocalHost: AnsiString read GetLocalHost;

property LocalAddress: AnsiString read GetLocalAddress;

property LocalPort: Integer read GetLocalPort;

property RemoteHost: AnsiString read GetRemoteHost;

property RemoteAddress: AnsiString read GetRemoteAddress;

property RemotePort: Integer read GetRemotePort;

property RemoteAddr: TSockAddrIn read GetRemoteAddr;

property Connected: Boolean read FConnected;

property Addr: TSockAddrIn read FAddr;

property ASyncStyles: TAsyncStyles read FAsyncStyles write SetAsyncStyles;

property Handle: HWnd read GetHandle;

property SocketHandle: TSocket read FSocket;

property LookupState: TLookupState read FLookupState;

property OnSocketEvent: TSocketEventEvent read FOnSocketEvent write FOnSocketEvent;

property OnErrorEvent: TSocketErrorEvent read FOnErrorEvent write FOnErrorEvent;

property Data: Pointer read FData write FData;

end;

TClientWinSocket = class(TCustomWinSocket)

private

FClientType: TClientType;

protected

procedure SetClientType(Value: TClientType);

public

procedure Connect(Socket: TSocket); override;

property ClientType: TClientType read FClientType write SetClientType;

end;

TServerClientWinSocket = class(TCustomWinSocket)

private

FServerWinSocket: TServerWinSocket;

public

constructor Create(Socket: TSocket; ServerWinSocket: TServerWinSocket);

destructor Destroy; override;

property ServerWinSocket: TServerWinSocket read FServerWinSocket;

end;

TThreadNotifyEvent = procedure (Sender: TObject;

Thread: TServerClientThread) of object;

TServerWinSocket = class(TCustomWinSocket)

private

FServerType: TServerType;

FThreadCacheSize: Integer;

FConnections: TList;

FActiveThreads: TList;

FListLock: TCriticalSection;

FServerAcceptThread: TServerAcceptThread;

FOnGetSocket: TGetSocketEvent;

FOnGetThread: TGetThreadEvent;

FOnThreadStart: TThreadNotifyEvent;

FOnThreadEnd: TThreadNotifyEvent;

FOnClientConnect: TSocketNotifyEvent;

FOnClientDisconnect: TSocketNotifyEvent;

FOnClientRead: TSocketNotifyEvent;

FOnClientWrite: TSocketNotifyEvent;

FOnClientError: TSocketErrorEvent;

procedure AddClient(AClient: TServerClientWinSocket);

procedure RemoveClient(AClient: TServerClientWinSocket);

procedure AddThread(AThread: TServerClientThread);

procedure RemoveThread(AThread: TServerClientThread);

procedure ClientEvent(Sender: TObject; Socket: TCustomWinSocket;

SocketEvent: TSocketEvent);

procedure ClientError(Sender: TObject; Socket: TCustomWinSocket;

ErrorEvent: TErrorEvent; var ErrorCode: Integer);

function GetActiveConnections: Integer;

function GetActiveThreads: Integer;

function GetConnections(Index: Integer): TCustomWinSocket;

function GetIdleThreads: Integer;

protected

function DoCreateThread(ClientSocket: TServerClientWinSocket): TServerClientThread; virtual;

procedure Listen(var Name, Address, Service: AnsiString; Port: Word;

QueueSize: Integer);

procedure SetServerType(Value: TServerType);

procedure SetThreadCacheSize(Value: Integer);

procedure ThreadEnd(AThread: TServerClientThread); dynamic;

procedure ThreadStart(AThread: TServerClientThread); dynamic;

function GetClientSocket(Socket: TSocket): TServerClientWinSocket; dynamic;

function GetServerThread(ClientSocket: TServerClientWinSocket): TServerClientThread; dynamic;

procedure ClientRead(Socket: TCustomWinSocket); dynamic;

procedure ClientWrite(Socket: TCustomWinSOcket); dynamic;

procedure ClientConnect(Socket: TCustomWinSOcket); dynamic;

procedure ClientDisconnect(Socket: TCustomWinSOcket); dynamic;

procedure ClientErrorEvent(Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;

var ErrorCode: Integer); dynamic;

public

constructor Create(ASocket: TSocket);

destructor Destroy; override;

procedure Accept(Socket: TSocket); override;

procedure Disconnect(Socket: TSocket); override;

function GetClientThread(ClientSocket: TServerClientWinSocket): TServerClientThread;

property ActiveConnections: Integer read GetActiveConnections;

property ActiveThreads: Integer read GetActiveThreads;

property Connections[Index: Integer]: TCustomWinSocket read GetConnections;

property IdleThreads: Integer read GetIdleThreads;

property ServerType: TServerType read FServerType write SetServerType;

property ThreadCacheSize: Integer read FThreadCacheSize write SetThreadCacheSize;

property OnGetSocket: TGetSocketEvent read FOnGetSocket write FOnGetSocket;

property OnGetThread: TGetThreadEvent read FOnGetThread write FOnGetThread;

property OnThreadStart: TThreadNotifyEvent read FOnThreadStart write FOnThreadStart;

property OnThreadEnd: TThreadNotifyEvent read FOnThreadEnd write FOnThreadEnd;

property OnClientConnect: TSocketNotifyEvent read FOnClientConnect write FOnClientConnect;

property OnClientDisconnect: TSocketNotifyEvent read FOnClientDisconnect write FOnClientDisconnect;

property OnClientRead: TSocketNotifyEvent read FOnClientRead write FOnClientRead;

property OnClientWrite: TSocketNotifyEvent read FOnClientWrite write FOnClientWrite;

property OnClientError: TSocketErrorEvent read FOnClientError write FOnClientError;

end;

TServerAcceptThread = class(TThread)

private

FServerSocket: TServerWinSocket;

public

constructor Create(CreateSuspended: Boolean; ASocket: TServerWinSocket);

procedure Execute; override;

property ServerSocket: TServerWinSocket read FServerSocket;

end;

TServerClientThread = class(TThread)

private

FClientSocket: TServerClientWinSocket;

FServerSocket: TServerWinSocket;

FException: Exception;

FEvent: TSimpleEvent;

FKeepInCache: Boolean;

FData: Pointer;

procedure HandleEvent(Sender: TObject; Socket: TCustomWinSocket;

SocketEvent: TSocketEvent);

procedure HandleError(Sender: TObject; Socket: TCustomWinSocket;

ErrorEvent: TErrorEvent; var ErrorCode: Integer);

procedure DoHandleException;

procedure DoRead;

procedure DoWrite;

protected

procedure DoTerminate; override;

procedure Execute; override;

procedure ClientExecute; virtual;

procedure Event(SocketEvent: TSocketEvent); virtual;

procedure Error(ErrorEvent: TErrorEvent; var ErrorCode: Integer); virtual;

procedure HandleException; virtual;

procedure ReActivate(ASocket: TServerClientWinSocket);

function StartConnect: Boolean;

function EndConnect: Boolean;

public

constructor Create(CreateSuspended: Boolean; ASocket: TServerClientWinSocket);

destructor Destroy; override;

property ClientSocket: TServerClientWinSocket read FClientSocket;

property ServerSocket: TServerWinSocket read FServerSocket;

property KeepInCache: Boolean read FKeepInCache write FKeepInCache;

property Data: Pointer read FData write FData;

end;

TAbstractSocket = class(TComponent)

private

FActive: Boolean;

FPort: Integer;

FAddress: AnsiString;

FHost: AnsiString;

FService: AnsiString;

procedure DoEvent(Sender: TObject; Socket: TCustomWinSocket;

SocketEvent: TSocketEvent);

procedure DoError(Sender: TObject; Socket: TCustomWinSocket;

ErrorEvent: TErrorEvent; var ErrorCode: Integer);

protected

procedure Event(Socket: TCustomWinSocket; SocketEvent: TSocketEvent);

virtual; abstract;

procedure Error(Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;

var ErrorCode: Integer); virtual; abstract;

procedure DoActivate(Value: Boolean); virtual; abstract;

procedure InitSocket(Socket: TCustomWinSocket);

procedure Loaded; override;

procedure SetActive(Value: Boolean);

procedure SetAddress(Value: AnsiString);

procedure SetHost(Value: AnsiString);

procedure SetPort(Value: Integer);

procedure SetService(Value: AnsiString);

property Active: Boolean read FActive write SetActive;

property Address: AnsiString read FAddress write SetAddress;

property Host: AnsiString read FHost write SetHost;

property Port: Integer read FPort write SetPort;

property Service: AnsiString read FService write SetService;

public

procedure Open;

procedure Close;

end;

TCustomSocket = class(TAbstractSocket)

private

FOnLookup: TSocketNotifyEvent;

FOnConnect: TSocketNotifyEvent;

FOnConnecting: TSocketNotifyEvent;

FOnDisconnect: TSocketNotifyEvent;

FOnListen: TSocketNotifyEvent;

FOnAccept: TSocketNotifyEvent;

FOnRead: TSocketNotifyEvent;

FOnWrite: TSocketNotifyEvent;

FOnError: TSocketErrorEvent;

protected

procedure Event(Socket: TCustomWinSocket; SocketEvent: TSocketEvent); override;

procedure Error(Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;

var ErrorCode: Integer); override;

property OnLookup: TSocketNotifyEvent read FOnLookup write FOnLookup;

property OnConnecting: TSocketNotifyEvent read FOnConnecting write FOnConnecting;

property OnConnect: TSocketNotifyEvent read FOnConnect write FOnConnect;

property OnDisconnect: TSocketNotifyEvent read FOnDisconnect write FOnDisconnect;

property OnListen: TSocketNotifyEvent read FOnListen write FOnListen;

property OnAccept: TSocketNotifyEvent read FOnAccept write FOnAccept;

property OnRead: TSocketNotifyEvent read FOnRead write FOnRead;

property OnWrite: TSocketNotifyEvent read FOnWrite write FOnWrite;

property OnError: TSocketErrorEvent read FOnError write FOnError;

end;

TWinSocketStream = class(TStream)

private

FSocket: TCustomWinSocket;

FTimeout: Longint;

FEvent: TSimpleEvent;

public

constructor Create(ASocket: TCustomWinSocket; TimeOut: Longint);

destructor Destroy; override;

function WaitForData(Timeout: Longint): Boolean;

function Read(var Buffer; Count: Longint): Longint; override;

function Write(const Buffer; Count: Longint): Longint; override;

function Seek(Offset: Longint; Origin: Word): Longint; override;

property TimeOut: Longint read FTimeout write FTimeout;

end;

TClientSocket = class(TCustomSocket)

private

FClientSocket: TClientWinSocket;

protected

procedure DoActivate(Value: Boolean); override;

function GetClientType: TClientType;

procedure SetClientType(Value: TClientType);

public

constructor Create(AOwner: TComponent); override;

destructor Destroy; override;

property Socket: TClientWinSocket read FClientSocket;

published

property Active;

property Address;

property ClientType: TClientType read GetClientType write SetClientType;

property Host;

property Port;

property Service;

property OnLookup;

property OnConnecting;

property OnConnect;

property OnDisconnect;

property OnRead;

property OnWrite;

property OnError;

end;

TCustomServerSocket = class(TCustomSocket)

protected

FServerSocket: TServerWinSocket;

procedure DoActivate(Value: Boolean); override;

function GetServerType: TServerType;

function GetGetThreadEvent: TGetThreadEvent;

function GetGetSocketEvent: TGetSocketEvent;

function GetThreadCacheSize: Integer;

function GetOnThreadStart: TThreadNotifyEvent;

function GetOnThreadEnd: TThreadNotifyEvent;

function GetOnClientEvent(Index: Integer): TSocketNotifyEvent;

function GetOnClientError: TSocketErrorEvent;

procedure SetServerType(Value: TServerType);

procedure SetGetThreadEvent(Value: TGetThreadEvent);

procedure SetGetSocketEvent(Value: TGetSocketEvent);

procedure SetThreadCacheSize(Value: Integer);

procedure SetOnThreadStart(Value: TThreadNotifyEvent);

procedure SetOnThreadEnd(Value: TThreadNotifyEvent);

procedure SetOnClientEvent(Index: Integer; Value: TSocketNotifyEvent);

procedure SetOnClientError(Value: TSocketErrorEvent);

property ServerType: TServerType read GetServerType write SetServerType;

property ThreadCacheSize: Integer read GetThreadCacheSize

write SetThreadCacheSize;

property OnGetThread: TGetThreadEvent read GetGetThreadEvent

write SetGetThreadEvent;

property OnGetSocket: TGetSocketEvent read GetGetSocketEvent

write SetGetSocketEvent;

property OnThreadStart: TThreadNotifyEvent read GetOnThreadStart

write SetOnThreadStart;

property OnThreadEnd: TThreadNotifyEvent read GetOnThreadEnd

write SetOnThreadEnd;

property OnClientConnect: TSocketNotifyEvent index 2 read GetOnClientEvent

write SetOnClientEvent;

property OnClientDisconnect: TSocketNotifyEvent index 3 read GetOnClientEvent

write SetOnClientEvent;

property OnClientRead: TSocketNotifyEvent index 0 read GetOnClientEvent

write SetOnClientEvent;

property OnClientWrite: TSocketNotifyEvent index 1 read GetOnClientEvent

write SetOnClientEvent;

property OnClientError: TSocketErrorEvent read GetOnClientError write SetOnClientError;

public

destructor Destroy; override;

end;

TServerSocket = class(TCustomServerSocket)

public

constructor Create(AOwner: TComponent); override;

property Socket: TServerWinSocket read FServerSocket;

published

property Active;

property Address;//Jacky

property Port;

property Host;//Jacky

property Service;

property ServerType;

property ThreadCacheSize default 10;

property OnListen;

property OnAccept;

property OnGetThread;

property OnGetSocket;

property OnThreadStart;

property OnThreadEnd;

property OnClientConnect;

property OnClientDisconnect;

property OnClientRead;

property OnClientWrite;

property OnClientError;

end;

TSocketErrorProc = procedure (ErrorCode: Integer);

function SetErrorProc(ErrorProc: TSocketErrorProc): TSocketErrorProc;

procedure Register;

implementation

uses RTLConsts;

threadvar

SocketErrorProc: TSocketErrorProc;

var

WSAData: TWSAData;

function SetErrorProc(ErrorProc: TSocketErrorProc): TSocketErrorProc;

begin

Result := SocketErrorProc;

SocketErrorProc := ErrorProc;

end;

function TCustomWinSocket.CheckSocketResult(ResultCode: Integer; const Op: AnsiString): Integer;

begin

if ResultCode <> 0 then begin

Result := WSAGetLastError;

if Result <> WSAEWOULDBLOCK then begin

Error(Self,eeConnect,ResultCode);

if ResultCode <> 0 then

raise ESocketError.CreateResFmt(@sWindowsSocketError,

[SysErrorMessage(Result), Result, Op]);

{

if Assigned(SocketErrorProc) then

SocketErrorProc(Result)

else raise ESocketError.CreateResFmt(@sWindowsSocketError,

[SysErrorMessage(Result), Result, Op]);

}

end;

end else Result := 0;

end;

procedure Startup;

var

ErrorCode: Integer;

begin

ErrorCode := WSAStartup($0202, WSAData);

if ErrorCode <> 0 then

raise ESocketError.CreateResFmt(@sWindowsSocketError,

[SysErrorMessage(ErrorCode), ErrorCode, 'WSAStartup']);

end;

procedure Cleanup;

var

ErrorCode: Integer;

begin

ErrorCode := WSACleanup;

if ErrorCode <> 0 then

raise ESocketError.CreateResFmt(@sWindowsSocketError,

[SysErrorMessage(ErrorCode), ErrorCode, 'WSACleanup']);

end;

{ TCustomWinSocket }

constructor TCustomWinSocket.Create(ASocket: TSocket);

begin

inherited Create;

Startup;

FSocketLock := TCriticalSection.Create;

FASyncStyles := [asRead, asWrite, asConnect, asClose];

FSocket := ASocket;

FAddr.sin_family := PF_INET;

FAddr.sin_addr.s_addr := INADDR_ANY;

FAddr.sin_port := 0;

FConnected := FSocket <> INVALID_SOCKET;

end;

destructor TCustomWinSocket.Destroy;

begin

FOnSocketEvent := nil; { disable events }

if FConnected and (FSocket <> INVALID_SOCKET) then

Disconnect(FSocket);

if FHandle <> 0 then DeallocateHWnd(FHandle);

FSocketLock.Free;

Cleanup;

FreeMem(FGetHostData);

FGetHostData := nil;

inherited Destroy;

end;

procedure TCustomWinSocket.Accept(Socket: TSocket);

begin

end;

procedure TCustomWinSocket.AsyncInitSocket(const Name, Address,

Service: AnsiString; Port: Word; QueueSize: Integer; Client: Boolean);

var

ErrorCode: Integer;

begin

try

case FLookupState of

lsIdle:

begin

FLookupState := lsLookupAddress;

FAddr.sin_addr.S_addr := INADDR_ANY;

if Name <> '' then

begin

if FGetHostData = nil then

FGetHostData := AllocMem(MAXGETHOSTSTRUCT);

FLookupHandle := WSAAsyncGetHostByName(Handle, CM_LOOKUPCOMPLETE,

PAnsiChar(Name), FGetHostData, MAXGETHOSTSTRUCT);

CheckSocketResult(Ord(FLookupHandle = 0), 'WSAASyncGetHostByName');

FService := Service;

FPort := Port;

FQueueSize := QueueSize;

FClient := Client;

FLookupState := lsLookupAddress;

Exit;

end else if Address <> '' then

begin

FLookupState := lsLookupAddress;

FAddr.sin_addr.S_addr := inet_addr(PAnsiChar(Address));

end else

begin

ErrorCode := 1110;

Error(Self, eeLookup, ErrorCode);

Disconnect(FSocket);

if ErrorCode <> 0 then

raise ESocketError.CreateRes(@sNoAddress);

Exit;

end;

end;

lsLookupAddress:

begin

if Service <> '' then

begin

if FGetHostData = nil then

FGetHostData := AllocMem(MAXGETHOSTSTRUCT);

FLookupHandle := WSAASyncGetServByName(Handle, CM_LOOKUPCOMPLETE,

PAnsiChar(Service), 'tcp' , FGetHostData, MAXGETHOSTSTRUCT);

CheckSocketResult(Ord(FLookupHandle = 0), 'WSAASyncGetServByName');

FLookupState := lsLookupService;

Exit;

end else

begin

FLookupState := lsLookupService;

FAddr.sin_port := htons(Port);

end;

end;

lsLookupService:

begin

FLookupState := lsIdle;

if Client then

DoOpen

else DoListen(QueueSize);

end;

end;

if FLookupState <> lsIdle then

ASyncInitSocket(Name, Address, Service, Port, QueueSize, Client);

except

Disconnect(FSocket);

raise;

end;

end;

procedure TCustomWinSocket.Close;

begin

Disconnect(FSocket);

end;

procedure TCustomWinSocket.Connect(Socket: TSocket);

begin

end;

procedure TCustomWinSocket.Lock;

begin

FSocketLock.Enter;

end;

procedure TCustomWinSocket.Unlock;

begin

FSocketLock.Leave;

end;

procedure TCustomWinSocket.CMSocketMessage(var Message: TCMSocketMessage);

function CheckError: Boolean;

var

ErrorEvent: TErrorEvent;

ErrorCode: Integer;

begin

if Message.SelectError <> 0 then

begin

Result := False;

ErrorCode := Message.SelectError;

case Message.SelectEvent of

FD_CONNECT: ErrorEvent := eeConnect;

FD_CLOSE: ErrorEvent := eeDisconnect;

FD_READ: ErrorEvent := eeReceive;

FD_WRITE: ErrorEvent := eeSend;

FD_ACCEPT: ErrorEvent := eeAccept;

else

ErrorEvent := eeGeneral;

end;

Error(Self, ErrorEvent, ErrorCode);

if ErrorCode <> 0 then

// raise ESocketError.CreateResFmt(@sASyncSocketError, [ErrorCode]);

end else Result := True;

end;

begin

with Message do

if CheckError then

case SelectEvent of

FD_CONNECT: Connect(Socket);

FD_CLOSE: Disconnect(Socket);

FD_READ: Read(Socket);

FD_WRITE: Write(Socket);

FD_ACCEPT: Accept(Socket);

end;

end;

procedure TCustomWinSocket.CMDeferFree(var Message);

begin

Free;

end;

procedure TCustomWinSocket.DeferFree;

begin

if FHandle <> 0 then PostMessage(FHandle, CM_DEFERFREE, 0, 0);

end;

procedure TCustomWinSocket.DoSetAsyncStyles;

var

Msg: Integer;

Wnd: HWnd;

Blocking: Longint;

begin

Msg := 0;

Wnd := 0;

if FAsyncStyles <> [] then

begin

Msg := CM_SOCKETMESSAGE;

Wnd := Handle;

end;

WSAAsyncSelect(FSocket, Wnd, Msg, Longint(Byte(FAsyncStyles)));

if FASyncStyles = [] then

begin

Blocking := 0;

ioctlsocket(FSocket, FIONBIO, Blocking);

end;

end;

procedure TCustomWinSocket.DoListen(QueueSize: Integer);

begin

CheckSocketResult(bind(FSocket, FAddr, SizeOf(FAddr)), 'bind');

DoSetASyncStyles;

if QueueSize > SOMAXCONN then QueueSize := SOMAXCONN;

Event(Self, seListen);

CheckSocketResult(Winsock.listen(FSocket, QueueSize), 'listen');

FLookupState := lsIdle;

FConnected := True;

end;

procedure TCustomWinSocket.DoOpen;

begin

DoSetASyncStyles;

Event(Self, seConnecting);

CheckSocketResult(WinSock.connect(FSocket, FAddr, SizeOf(FAddr)), 'connect');

FLookupState := lsIdle;

if not (asConnect in FAsyncStyles) then

begin

FConnected := FSocket <> INVALID_SOCKET;

Event(Self, seConnect);

end;

end;

function TCustomWinSocket.GetHandle: HWnd;

begin

if FHandle = 0 then

FHandle := AllocateHwnd(WndProc);

Result := FHandle;

end;

function TCustomWinSocket.GetLocalAddress: AnsiString;

var

SockAddrIn: TSockAddrIn;

Size: Integer;

begin

Lock;

try

Result := '';

if FSocket = INVALID_SOCKET then Exit;

Size := SizeOf(SockAddrIn);

if getsockname(FSocket, SockAddrIn, Size) = 0 then

Result := inet_ntoa(SockAddrIn.sin_addr);

finally

Unlock;

end;

end;

function TCustomWinSocket.GetLocalHost: AnsiString;

var

LocalName: array[0..255] of AnsiChar;

begin

Lock;

try

Result := '';

if FSocket = INVALID_SOCKET then Exit;

if gethostname(LocalName, SizeOf(LocalName)) = 0 then

Result := LocalName;

finally

Unlock;

end;

end;

function TCustomWinSocket.GetLocalPort: Integer;

var

SockAddrIn: TSockAddrIn;

Size: Integer;

begin

Lock;

try

Result := -1;

if FSocket = INVALID_SOCKET then Exit;

Size := SizeOf(SockAddrIn);

if getsockname(FSocket, SockAddrIn, Size) = 0 then

Result := ntohs(SockAddrIn.sin_port);

finally

Unlock;

end;

end;

function TCustomWinSocket.GetRemoteHost: AnsiString;

var

SockAddrIn: TSockAddrIn;

Size: Integer;

HostEnt: PHostEnt;

begin

Lock;

try

Result := '';

if not FConnected then Exit;

Size := SizeOf(SockAddrIn);

CheckSocketResult(getpeername(FSocket, SockAddrIn, Size), 'getpeername');

HostEnt := gethostbyaddr(@SockAddrIn.sin_addr.s_addr, 4, PF_INET);

if HostEnt <> nil then Result := HostEnt.h_name;

finally

Unlock;

end;

end;

function TCustomWinSocket.GetRemoteAddress: AnsiString;

var

SockAddrIn: TSockAddrIn;

Size: Integer;

begin

Lock;

try

Result := '';

if not FConnected then Exit;

Size := SizeOf(SockAddrIn);

CheckSocketResult(getpeername(FSocket, SockAddrIn, Size), 'getpeername');

Result := inet_ntoa(SockAddrIn.sin_addr);

finally

Unlock;

end;

end;

function TCustomWinSocket.GetRemotePort: Integer;

var

SockAddrIn: TSockAddrIn;

Size: Integer;

begin

Lock;

try

Result := 0;

if not FConnected then Exit;

Size := SizeOf(SockAddrIn);

CheckSocketResult(getpeername(FSocket, SockAddrIn, Size), 'getpeername');

Result := ntohs(SockAddrIn.sin_port);

finally

Unlock;

end;

end;

function TCustomWinSocket.GetRemoteAddr: TSockAddrIn;

var

Size: Integer;

begin

Lock;

try

FillChar(Result, SizeOf(Result), 0);

if not FConnected then Exit;

Size := SizeOf(Result);

if getpeername(FSocket, Result, Size) <> 0 then

FillChar(Result, SizeOf(Result), 0);

finally

Unlock;

end;

end;

function TCustomWinSocket.LookupName(const Name: AnsiString): TInAddr;

var

HostEnt: PHostEnt;

InAddr: TInAddr;

begin

HostEnt := gethostbyname(PAnsiChar(Name));

FillChar(InAddr, SizeOf(InAddr), 0);

if HostEnt <> nil then

begin

with InAddr, HostEnt^ do

begin

S_un_b.s_b1 := h_addr^[0];

S_un_b.s_b2 := h_addr^[1];

S_un_b.s_b3 := h_addr^[2];

S_un_b.s_b4 := h_addr^[3];

end;

end;

Result := InAddr;

end;

function TCustomWinSocket.LookupService(const Service: AnsiString): Integer;

var

ServEnt: PServEnt;

begin

ServEnt := getservbyname(PAnsiChar(Service), 'tcp');

if ServEnt <> nil then

Result := ntohs(ServEnt.s_port)

else Result := 0;

end;

function TCustomWinSocket.InitSocket(const Name, Address, Service: AnsiString; Port: Word;

Client: Boolean): TSockAddrIn;

begin

Result.sin_family := PF_INET;

if Name <> '' then

Result.sin_addr := LookupName(name)

else if Address <> '' then

Result.sin_addr.s_addr := inet_addr(PAnsiChar(Address))

else if not Client then

Result.sin_addr.s_addr := INADDR_ANY

else raise ESocketError.CreateRes(@sNoAddress);

if Service <> '' then

Result.sin_port := htons(LookupService(Service))

else

Result.sin_port := htons(Port);

end;

procedure TCustomWinSocket.Listen(const Name, Address, Service: AnsiString; Port: Word;

QueueSize: Integer; Block: Boolean);

begin

if FConnected then raise ESocketError.CreateRes(@sCannotListenOnOpen);

FSocket := socket(PF_INET, SOCK_STREAM, IPPROTO_IP);

if FSocket = INVALID_SOCKET then raise ESocketError.CreateRes(@sCannotCreateSocket);

try

Event(Self, seLookUp);

if Block then

begin

FAddr := InitSocket(Name, Address, Service, Port, False);

DoListen(QueueSize);

end else

AsyncInitSocket(Name, Address, Service, Port, QueueSize, False);

except

Disconnect(FSocket);

raise;

end;

end;

procedure TCustomWinSocket.Open(const Name, Address, Service: AnsiString; Port: Word; Block: Boolean);

begin

if FConnected then raise ESocketError.CreateRes(@sSocketAlreadyOpen);

FSocket := socket(PF_INET, SOCK_STREAM, IPPROTO_IP);

if FSocket = INVALID_SOCKET then raise ESocketError.CreateRes(@sCannotCreateSocket);

try

Event(Self, seLookUp);

if Block then

begin

FAddr := InitSocket(Name, Address, Service, Port, True);

DoOpen;

end else

AsyncInitSocket(Name, Address, Service, Port, 0, True);

except

Disconnect(FSocket);

raise;

end;

end;

procedure TCustomWinSocket.Disconnect(Socket: TSocket);

begin

Lock;

try

if FLookupHandle <> 0 then

CheckSocketResult(WSACancelASyncRequest(FLookupHandle), 'WSACancelASyncRequest');

FLookupHandle := 0;

if (Socket = INVALID_SOCKET) or (Socket <> FSocket) then exit;

Event(Self, seDisconnect);

CheckSocketResult(closesocket(FSocket), 'closesocket');

FSocket := INVALID_SOCKET;

FAddr.sin_family := PF_INET;

FAddr.sin_addr.s_addr := INADDR_ANY;

FAddr.sin_port := 0;

FConnected := False;

FreeAndNil(FSendStream);

finally

Unlock;

end;

end;

procedure TCustomWinSocket.DefaultHandler(var Message);

begin

with TMessage(Message) do

if FHandle <> 0 then

Result := CallWindowProc(@DefWindowProc, FHandle, Msg, wParam, lParam);

end;

procedure TCustomWinSocket.Event(Socket: TCustomWinSocket; SocketEvent: TSocketEvent);

begin

if Assigned(FOnSocketEvent) then FOnSocketEvent(Self, Socket, SocketEvent);

end;

procedure TCustomWinSocket.Error(Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;

var ErrorCode: Integer);

begin

if Assigned(FOnErrorEvent) then FOnErrorEvent(Self, Socket, ErrorEvent, ErrorCode);

end;

function TCustomWinSocket.SendText(const s: AnsiString): Integer;

begin

Result := SendBuf(Pointer(S)^, Length(S));

end;

function TCustomWinSocket.SendStreamPiece: Boolean;

var

Buffer: array[0..4095] of Byte;

StartPos: Integer;

AmountInBuf: Integer;

AmountSent: Integer;

ErrorCode: Integer;

procedure DropStream;

begin

if FDropAfterSend then Disconnect(FSocket);

FDropAfterSend := False;

FSendStream.Free;

FSendStream := nil;

end;

begin

Lock;

try

Result := False;

if FSendStream <> nil then

begin

if (FSocket = INVALID_SOCKET) or (not FConnected) then exit;

while True do

begin

StartPos := FSendStream.Position;

AmountInBuf := FSendStream.Read(Buffer, SizeOf(Buffer));

if AmountInBuf > 0 then

begin

AmountSent := send(FSocket, Buffer, AmountInBuf, 0);

if AmountSent = SOCKET_ERROR then

begin

ErrorCode := WSAGetLastError;

if ErrorCode <> WSAEWOULDBLOCK then

begin

Error(Self, eeSend, ErrorCode);

Disconnect(FSocket);

DropStream;

if FAsyncStyles <> [] then Abort;

Break;

end else

begin

FSendStream.Position := StartPos;

Break;

end;

end else if AmountInBuf > AmountSent then

FSendStream.Position := StartPos + AmountSent

else if FSendStream.Position = FSendStream.Size then

begin

DropStream;

Break;

end;

end else

begin

DropStream;

Break;

end;

end;

Result := True;

end;

finally

Unlock;

end;

end;

function TCustomWinSocket.SendStream(AStream: TStream): Boolean;

begin

Result := False;

if FSendStream = nil then

begin

FSendStream := AStream;

Result := SendStreamPiece;

end;

end;

function TCustomWinSocket.SendStreamThenDrop(AStream: TStream): Boolean;

begin

FDropAfterSend := True;

Result := SendStream(AStream);

if not Result then FDropAfterSend := False;

end;

function TCustomWinSocket.SendBuf(var Buf; Count: Integer): Integer;

var

ErrorCode: Integer;

begin

Lock;

try

Result := 0;

if not FConnected then Exit;

Result := send(FSocket, Buf, Count, 0);

if Result = SOCKET_ERROR then

begin

ErrorCode := WSAGetLastError;

if (ErrorCode <> WSAEWOULDBLOCK) then

begin

Error(Self, eeSend, ErrorCode);

Disconnect(FSocket);

if ErrorCode <> 0 then

raise ESocketError.CreateResFmt(@sWindowsSocketError,

[SysErrorMessage(ErrorCode), ErrorCode, 'send']);

end;

end;

finally

Unlock;

end;

end;

procedure TCustomWinSocket.SetAsyncStyles(Value: TASyncStyles);

begin

if Value <> FASyncStyles then

begin

FASyncStyles := Value;

if FSocket <> INVALID_SOCKET then

DoSetAsyncStyles;

end;

end;

procedure TCustomWinSocket.Read(Socket: TSocket);

begin

if (FSocket = INVALID_SOCKET) or (Socket <> FSocket) then Exit;

Event(Self, seRead);

end;

function TCustomWinSocket.ReceiveBuf(var Buf; Count: Integer): Integer;

var

ErrorCode: Integer;

begin

Lock;

try

Result := 0;

if (Count = -1) and FConnected then

ioctlsocket(FSocket, FIONREAD, Longint(Result))

else begin

if not FConnected then Exit;

Result := recv(FSocket, Buf, Count, 0);

if Result = SOCKET_ERROR then

begin

ErrorCode := WSAGetLastError;

if ErrorCode <> WSAEWOULDBLOCK then

begin

Error(Self, eeReceive, ErrorCode);

Disconnect(FSocket);

if ErrorCode <> 0 then

raise ESocketError.CreateResFmt(@sWindowsSocketError,

[SysErrorMessage(ErrorCode), ErrorCode, 'recv']);

end;

end;

end;

finally

Unlock;

end;

end;

function TCustomWinSocket.ReceiveLength: Integer;

begin

Result := ReceiveBuf(Pointer(nil)^, -1);

end;

function TCustomWinSocket.ReceiveText: AnsiString;

begin

SetLength(Result, ReceiveBuf(Pointer(nil)^, -1));

SetLength(Result, ReceiveBuf(Pointer(Result)^, Length(Result)));

end;

procedure TCustomWinSocket.WndProc(var Message: TMessage);

begin

try

Dispatch(Message);

except

if Assigned(ApplicationHandleException) then

ApplicationHandleException(Self);

end;

end;

procedure TCustomWinSocket.Write(Socket: TSocket);

begin

if (FSocket = INVALID_SOCKET) or (Socket <> FSocket) then Exit;

if not SendStreamPiece then Event(Self, seWrite);

end;

procedure TCustomWinSocket.CMLookupComplete(var Message: TCMLookupComplete);

var

ErrorCode: Integer;

begin

if Message.LookupHandle = FLookupHandle then

begin

FLookupHandle := 0;

if Message.AsyncError <> 0 then

begin

ErrorCode := Message.AsyncError;

Error(Self, eeLookup, ErrorCode);

Disconnect(FSocket);

if ErrorCode <> 0 then

raise ESocketError.CreateResFmt(@sWindowsSocketError,

[SysErrorMessage(Message.AsyncError), Message.ASyncError, 'ASync Lookup']);

Exit;

end;

if FLookupState = lsLookupAddress then

begin

FAddr.sin_addr.S_addr := Integer(Pointer(PHostEnt(FGetHostData).h_addr^)^);

ASyncInitSocket('', '', FService, FPort, FQueueSize, FClient);

end else if FLookupState = lsLookupService then

begin

FAddr.sin_port := PServEnt(FGetHostData).s_port;

FPort := 0;

FService := '';

ASyncInitSocket('', '', '', 0, FQueueSize, FClient);

end;

end;

end;

{ TClientWinSocket }

procedure TClientWinSocket.Connect(Socket: TSocket);

begin

FConnected := True;

Event(Self, seConnect);

end;

procedure TClientWinSocket.SetClientType(Value: TClientType);

begin

if Value <> FClientType then

if not FConnected then

begin

FClientType := Value;

if FClientType = ctBlocking then

ASyncStyles := []

else ASyncStyles := [asRead, asWrite, asConnect, asClose];

end else raise ESocketError.CreateRes(@sCantChangeWhileActive);

end;

{ TServerClientWinsocket }

constructor TServerClientWinSocket.Create(Socket: TSocket; ServerWinSocket: TServerWinSocket);

begin

FServerWinSocket := ServerWinSocket;

if Assigned(FServerWinSocket) then

begin

FServerWinSocket.AddClient(Self);

if FServerWinSocket.AsyncStyles <> [] then

begin

OnSocketEvent := FServerWinSocket.ClientEvent;

OnErrorEvent := FServerWinSocket.ClientError;

end;

end;

inherited Create(Socket);

if FServerWinSocket.ASyncStyles <> [] then DoSetAsyncStyles;

if FConnected then Event(Self, seConnect);

end;

destructor TServerClientWinSocket.Destroy;

begin

if Assigned(FServerWinSocket) then

FServerWinSocket.RemoveClient(Self);

inherited Destroy;

end;

{ TServerWinSocket }

constructor TServerWinSocket.Create(ASocket: TSocket);

begin

FConnections := TList.Create;

FActiveThreads := TList.Create;

FListLock := TCriticalSection.Create;

inherited Create(ASocket);

FAsyncStyles := [asAccept];

end;

destructor TServerWinSocket.Destroy;

begin

inherited Destroy;

FConnections.Free;

FActiveThreads.Free;

FListLock.Free;

end;

procedure TServerWinSocket.AddClient(AClient: TServerClientWinSocket);

begin

FListLock.Enter;

try

if FConnections.IndexOf(AClient) < 0 then

FConnections.Add(AClient);

finally

FListLock.Leave;

end;

end;

procedure TServerWinSocket.RemoveClient(AClient: TServerClientWinSocket);

begin

FListLock.Enter;

try

FConnections.Remove(AClient);

finally

FListLock.Leave;

end;

end;

procedure TServerWinSocket.AddThread(AThread: TServerClientThread);

begin

FListLock.Enter;

try

if FActiveThreads.IndexOf(AThread) < 0 then

begin

FActiveThreads.Add(AThread);

if FActiveThreads.Count <= FThreadCacheSize then

AThread.KeepInCache := True;

end;

finally

FListLock.Leave;

end;

end;

procedure TServerWinSocket.RemoveThread(AThread: TServerClientThread);

begin

FListLock.Enter;

try

FActiveThreads.Remove(AThread);

finally

FListLock.Leave;

end;

end;

procedure TServerWinSocket.ClientEvent(Sender: TObject; Socket: TCustomWinSocket;

SocketEvent: TSocketEvent);

begin

case SocketEvent of

seAccept,

seLookup,

seConnecting,

seListen:

begin end;

seConnect: ClientConnect(Socket);

seDisconnect: ClientDisconnect(Socket);

seRead: ClientRead(Socket);

seWrite: ClientWrite(Socket);

end;

end;

procedure TServerWinSocket.ClientError(Sender: TObject; Socket: TCustomWinSocket;

ErrorEvent: TErrorEvent; var ErrorCode: Integer);

begin

ClientErrorEvent(Socket, ErrorEvent, ErrorCode);

end;

function TServerWinSocket.GetActiveConnections: Integer;

begin

Result := FConnections.Count;

end;

function TServerWinSocket.GetConnections(Index: Integer): TCustomWinSocket;

begin

Result := FConnections[Index];

end;

function TServerWinSocket.GetActiveThreads: Integer;

var

I: Integer;

begin

FListLock.Enter;

try

Result := 0;

for I := 0 to FActiveThreads.Count - 1 do

if TServerClientThread(FActiveThreads[I]).ClientSocket <> nil then

Inc(Result);

finally

FListLock.Leave;

end;

end;

function TServerWinSocket.GetIdleThreads: Integer;

var

I: Integer;

begin

FListLock.Enter;

try

Result := 0;

for I := 0 to FActiveThreads.Count - 1 do

if TServerClientThread(FActiveThreads[I]).ClientSocket = nil then

Inc(Result);

finally

FListLock.Leave;

end;

end;

procedure TServerWinSocket.Accept(Socket: TSocket);

var

ClientSocket: TServerClientWinSocket;

ClientWinSocket: TSocket;

Addr: TSockAddrIn;

Len: Integer;

OldOpenType, NewOpenType: Integer;

begin

Len := SizeOf(OldOpenType);

if getsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, PAnsiChar(@OldOpenType),

Len) = 0 then

try

if FServerType = stThreadBlocking then

begin

NewOpenType := SO_SYNCHRONOUS_NONALERT;

setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, PAnsiChar(@NewOpenType), Len);

end;

Len := SizeOf(Addr);

ClientWinSocket := WinSock.accept(Socket, @Addr, @Len);

if ClientWinSocket <> INVALID_SOCKET then

begin

ClientSocket := GetClientSocket(ClientWinSocket);

if Assigned(FOnSocketEvent) then

FOnSocketEvent(Self, ClientSocket, seAccept);

if FServerType = stThreadBlocking then

begin

ClientSocket.ASyncStyles := [];

GetServerThread(ClientSocket);

end;

end;

finally

Len := SizeOf(OldOpenType);

setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, PAnsiChar(@OldOpenType), Len);

end;

end;

procedure TServerWinSocket.Disconnect(Socket: TSocket);

var

SaveCacheSize: Integer;

begin

Lock;

try

SaveCacheSize := ThreadCacheSize;

try

ThreadCacheSize := 0;

while FActiveThreads.Count > 0 do

with TServerClientThread(FActiveThreads.Last) do

begin

FreeOnTerminate := False;

Terminate;

FEvent.SetEvent;

if (ClientSocket <> nil) and ClientSocket.Connected then

ClientSocket.Close;

WaitFor;

Free;

end;

while FConnections.Count > 0 do

TCustomWinSocket(FConnections.Last).Free;

if FServerAcceptThread <> nil then

FServerAcceptThread.Terminate;

inherited Disconnect(Socket);

FServerAcceptThread.Free;

FServerAcceptThread := nil;

finally

ThreadCacheSize := SaveCacheSize;

end;

finally

Unlock;

end;

end;

function TServerWinSocket.DoCreateThread(ClientSocket: TServerClientWinSocket): TServerClientThread;

begin

Result := TServerClientThread.Create(False, ClientSocket);

end;

procedure TServerWinSocket.Listen(var Name, Address, Service: AnsiString; Port: Word;

QueueSize: Integer);

begin

inherited Listen(Name, Address, Service, Port, QueueSize, ServerType = stThreadBlocking);

if FConnected and (ServerType = stThreadBlocking) then

FServerAcceptThread := TServerAcceptThread.Create(False, Self);

end;

procedure TServerWinSocket.SetServerType(Value: TServerType);

begin

if Value <> FServerType then

if not FConnected then

begin

FServerType := Value;

if FServerType = stThreadBlocking then

ASyncStyles := []

else ASyncStyles := [asAccept];

end else raise ESocketError.CreateRes(@sCantChangeWhileActive);

end;

procedure TServerWinSocket.SetThreadCacheSize(Value: Integer);

var

Start, I: Integer;

begin

if Value <> FThreadCacheSize then

begin

if Value < FThreadCacheSize then

Start := Value

else Start := FThreadCacheSize;

FThreadCacheSize := Value;

FListLock.Enter;

try

for I := 0 to FActiveThreads.Count - 1 do

TServerClientThread(FActiveThreads[I]).KeepInCache := I < Start;;

// with TServerClientThread(FActiveThreads[I]) do

// KeepInCache := I < Start;

finally

FListLock.Leave;

end;

end;

end;

function TServerWinSocket.GetClientSocket(Socket: TSocket): TServerClientWinSocket;

begin

Result := nil;

if Assigned(FOnGetSocket) then FOnGetSocket(Self, Socket, Result);

if Result = nil then

Result := TServerClientWinSocket.Create(Socket, Self);

end;

procedure TServerWinSocket.ThreadEnd(AThread: TServerClientThread);

begin

if Assigned(FOnThreadEnd) then FOnThreadEnd(Self, AThread);

end;

procedure TServerWinSocket.ThreadStart(AThread: TServerClientThread);

begin

if Assigned(FOnThreadStart) then FOnThreadStart(Self, AThread);

end;

function TServerWinSocket.GetServerThread(ClientSocket: TServerClientWinSocket): TServerClientThread;

var

I: Integer;

begin

Result := nil;

FListLock.Enter;

try

for I := 0 to FActiveThreads.Count - 1 do

if TServerClientThread(FActiveThreads[I]).ClientSocket = nil then

begin

Result := FActiveThreads[I];

Result.ReActivate(ClientSocket);

Break;

end;

finally

FListLock.Leave;

end;

if Result = nil then

begin

if Assigned(FOnGetThread) then FOnGetThread(Self, ClientSocket, Result);

if Result = nil then Result := DoCreateThread(ClientSocket);

end;

end;

function TServerWinSocket.GetClientThread(ClientSocket: TServerClientWinSocket): TServerClientThread;

var

I: Integer;

begin

Result := nil;

FListLock.Enter;

try

for I := 0 to FActiveThreads.Count - 1 do

if TServerClientThread(FActiveThreads[I]).ClientSocket = ClientSocket then

begin

Result := FActiveThreads[I];

Break;

end;

finally

FListLock.Leave;

end;

end;

procedure TServerWinSocket.ClientConnect(Socket: TCustomWinSocket);

begin

if Assigned(FOnClientConnect) then FOnClientConnect(Self, Socket);

end;

procedure TServerWinSocket.ClientDisconnect(Socket: TCustomWinSocket);

begin

if Assigned(FOnClientDisconnect) then FOnClientDisconnect(Self, Socket);

if ServerType = stNonBlocking then Socket.DeferFree;

end;

procedure TServerWinSocket.ClientRead(Socket: TCustomWinSocket);

begin

if Assigned(FOnClientRead) then FOnClientRead(Self, Socket);

end;

procedure TServerWinSocket.ClientWrite(Socket: TCustomWinSocket);

begin

if Assigned(FOnClientWrite) then FOnClientWrite(Self, Socket);

end;

procedure TServerWinSocket.ClientErrorEvent(Socket: TCustomWinSocket;

ErrorEvent: TErrorEvent; var ErrorCode: Integer);

begin

if Assigned(FOnClientError) then FOnClientError(Self, Socket, ErrorEvent, ErrorCode);

end;

{ TServerAcceptThread }

constructor TServerAcceptThread.Create(CreateSuspended: Boolean;

ASocket: TServerWinSocket);

begin

FServerSocket := ASocket;

inherited Create(CreateSuspended);

end;

procedure TServerAcceptThread.Execute;

begin

while not Terminated do

FServerSocket.Accept(FServerSocket.SocketHandle);

end;

{ TServerClientThread }

constructor TServerClientThread.Create(CreateSuspended: Boolean;

ASocket: TServerClientWinSocket);

begin

FreeOnTerminate := True;

FEvent := TSimpleEvent.Create;

inherited Create(True);

Priority := tpHigher;

ReActivate(ASocket);

if not CreateSuspended then Resume;

end;

destructor TServerClientThread.Destroy;

begin

FClientSocket.Free;

FEvent.Free;

inherited Destroy;

end;

procedure TServerClientThread.ReActivate(ASocket: TServerClientWinSocket);

begin

FClientSocket := ASocket;

if Assigned(FClientSocket) then

begin

FServerSocket := FClientSocket.ServerWinSocket;

FServerSocket.AddThread(Self);

FClientSocket.OnSocketEvent := HandleEvent;

FClientSocket.OnErrorEvent := HandleError;

FEvent.SetEvent;

end;

end;

procedure TServerClientThread.DoHandleException;

begin

if GetCapture <> 0 then SendMessage(GetCapture, WM_CANCELMODE, 0, 0);

if FException is Exception then

begin

if Assigned(ApplicationShowException) then

ApplicationShowException(FException);

end else

SysUtils.ShowException(FException, nil);

end;

procedure TServerClientThread.DoRead;

begin

ClientSocket.ServerWinSocket.Event(ClientSocket, seRead);

end;

procedure TServerClientThread.DoTerminate;

begin

inherited DoTerminate;

if Assigned(FServerSocket) then

FServerSocket.RemoveThread(Self);

end;

procedure TServerClientThread.DoWrite;

begin

FServerSocket.Event(ClientSocket, seWrite);

end;

procedure TServerClientThread.HandleEvent(Sender: TObject; Socket: TCustomWinSocket;

SocketEvent: TSocketEvent);

begin

Event(SocketEvent);

end;

procedure TServerClientThread.HandleError(Sender: TObject; Socket: TCustomWinSocket;

ErrorEvent: TErrorEvent; var ErrorCode: Integer);

begin

Error(ErrorEvent, ErrorCode);

end;

procedure TServerClientThread.Event(SocketEvent: TSocketEvent);

begin

FServerSocket.ClientEvent(Self, ClientSocket, SocketEvent);

end;

procedure TServerClientThread.Error(ErrorEvent: TErrorEvent; var ErrorCode: Integer);

begin

FServerSocket.ClientError(Self, ClientSocket, ErrorEvent, ErrorCode);

end;

procedure TServerClientThread.HandleException;

begin

FException := Exception(ExceptObject);

try

if not (FException is EAbort) then

Synchronize(DoHandleException);

finally

FException := nil;

end;

end;

procedure TServerClientThread.Execute;

begin

FServerSocket.ThreadStart(Self);

try

try

while True do

begin

if StartConnect then ClientExecute;

if EndConnect then Break;

end;

except

HandleException;

KeepInCache := False;

end;

finally

FServerSocket.ThreadEnd(Self);

end;

end;

procedure TServerClientThread.ClientExecute;

var

FDSet: TFDSet;

TimeVal: TTimeVal;

begin

while not Terminated and ClientSocket.Connected do

begin

FD_ZERO(FDSet);

FD_SET(ClientSocket.SocketHandle, FDSet);

TimeVal.tv_sec := 0;

TimeVal.tv_usec := 500;

if (select(0, @FDSet, nil, nil, @TimeVal) > 0) and not Terminated then

if ClientSocket.ReceiveBuf(FDSet, -1) = 0 then Break

else Synchronize(DoRead);

if (select(0, nil, @FDSet, nil, @TimeVal) > 0) and not Terminated then

Synchronize(DoWrite);

end;

end;

function TServerClientThread.StartConnect: Boolean;

begin

if FEvent.WaitFor(INFINITE) = wrSignaled then

FEvent.ResetEvent;

Result := not Terminated;

end;

function TServerClientThread.EndConnect: Boolean;

begin

FClientSocket.Free;

FClientSocket := nil;

Result := Terminated or not KeepInCache;

end;

{ TAbstractSocket }

procedure TAbstractSocket.DoEvent(Sender: TObject; Socket: TCustomWinSocket;

SocketEvent: TSocketEvent);

begin

Event(Socket, SocketEvent);

end;

procedure TAbstractSocket.DoError(Sender: TObject; Socket: TCustomWinSocket;

ErrorEvent: TErrorEvent; var ErrorCode: Integer);

begin

Error(Socket, ErrorEvent, ErrorCode);

end;

procedure TAbstractSocket.SetActive(Value: Boolean);

begin

if Value <> FActive then

begin

//if (csDesigning in ComponentState) or (csLoading in ComponentState) then

FActive := Value;

//if not (csLoading in ComponentState) then

DoActivate(Value);

end;

end;

procedure TAbstractSocket.InitSocket(Socket: TCustomWinSocket);

begin

Socket.OnSocketEvent := DoEvent;

Socket.OnErrorEvent := DoError;

end;

procedure TAbstractSocket.Loaded;

begin

inherited Loaded;

DoActivate(FActive);

end;

procedure TAbstractSocket.SetAddress(Value: AnsiString);

begin

if CompareText(Value, FAddress) <> 0 then

begin

if not (csLoading in ComponentState) and FActive then

raise ESocketError.CreateRes(@sCantChangeWhileActive);

FAddress := Value;

end;

end;

procedure TAbstractSocket.SetHost(Value: AnsiString);

begin

if CompareText(Value, FHost) <> 0 then

begin

if not (csLoading in ComponentState) and FActive then

raise ESocketError.CreateRes(@sCantChangeWhileActive);

FHost := Value;

end;

end;

procedure TAbstractSocket.SetPort(Value: Integer);

begin

if FPort <> Value then

begin

if not (csLoading in ComponentState) and FActive then

raise ESocketError.CreateRes(@sCantChangeWhileActive);

FPort := Value;

end;

end;

procedure TAbstractSocket.SetService(Value: AnsiString);

begin

if CompareText(Value, FService) <> 0 then

begin

if not (csLoading in ComponentState) and FActive then

raise ESocketError.CreateRes(@sCantChangeWhileActive);

FService := Value;

end;

end;

procedure TAbstractSocket.Open;

begin

Active := True;

end;

procedure TAbstractSocket.Close;

begin

Active := False;

end;

{ TCustomSocket }

procedure TCustomSocket.Event(Socket: TCustomWinSocket; SocketEvent: TSocketEvent);

begin

case SocketEvent of

seLookup: if Assigned(FOnLookup) then FOnLookup(Self, Socket);

seConnecting: if Assigned(FOnConnecting) then FOnConnecting(Self, Socket);

seConnect:

begin

FActive := True;

if Assigned(FOnConnect) then FOnConnect(Self, Socket);

end;

seListen:

begin

FActive := True;

if Assigned(FOnListen) then FOnListen(Self, Socket);

end;

seDisconnect:

begin

FActive := False;

if Assigned(FOnDisconnect) then FOnDisconnect(Self, Socket);

end;

seAccept: if Assigned(FOnAccept) then FOnAccept(Self, Socket);

seRead: if Assigned(FOnRead) then FOnRead(Self, Socket);

seWrite: if Assigned(FOnWrite) then FOnWrite(Self, Socket);

end;

end;

procedure TCustomSocket.Error(Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;

var ErrorCode: Integer);

begin

if Assigned(FOnError) then FOnError(Self, Socket, ErrorEvent, ErrorCode);

end;

{ TWinSocketStream }

constructor TWinSocketStream.Create(ASocket: TCustomWinSocket; TimeOut: Longint);

begin

if ASocket.ASyncStyles <> [] then

raise ESocketError.CreateRes(@sSocketMustBeBlocking);

FSocket := ASocket;

FTimeOut := TimeOut;

FEvent := TSimpleEvent.Create;

inherited Create;

end;

destructor TWinSocketStream.Destroy;

begin

FEvent.Free;

inherited Destroy;

end;

function TWinSocketStream.WaitForData(Timeout: Longint): Boolean;

var

FDSet: TFDSet;

TimeVal: TTimeVal;

begin

TimeVal.tv_sec := Timeout div 1000;

TimeVal.tv_usec := (Timeout mod 1000) * 1000;

FD_ZERO(FDSet);

FD_SET(FSocket.SocketHandle, FDSet);

Result := select(0, @FDSet, nil, nil, @TimeVal) > 0;

end;

function TWinSocketStream.Read(var Buffer; Count: Longint): Longint;

var

Overlapped: TOverlapped;

ErrorCode: Integer;

begin

FSocket.Lock;

try

FillChar(OVerlapped, SizeOf(Overlapped), 0);

Overlapped.hEvent := FEvent.Handle;

if not ReadFile(FSocket.SocketHandle, Buffer, Count, DWORD(Result),

@Overlapped) and (GetLastError <> ERROR_IO_PENDING) then

begin

ErrorCode := GetLastError;

raise ESocketError.CreateResFmt(@sSocketIOError, [sSocketRead, ErrorCode,

SysErrorMessage(ErrorCode)]);

end;

if FEvent.WaitFor(FTimeOut) <> wrSignaled then

Result := 0

else

begin

GetOverlappedResult(FSocket.SocketHandle, Overlapped, DWORD(Result), False);

FEvent.ResetEvent;

end;

finally

FSocket.Unlock;

end;

end;

function TWinSocketStream.Write(const Buffer; Count: Longint): Longint;

var

Overlapped: TOverlapped;

ErrorCode: Integer;

begin

FSocket.Lock;

try

FillChar(OVerlapped, SizeOf(Overlapped), 0);

Overlapped.hEvent := FEvent.Handle;

if not WriteFile(FSocket.SocketHandle, Buffer, Count, DWORD(Result),

@Overlapped) and (GetLastError <> ERROR_IO_PENDING) then

begin

ErrorCode := GetLastError;

raise ESocketError.CreateResFmt(@sSocketIOError, [sSocketWrite, ErrorCode,

SysErrorMessage(ErrorCode)]);

end;

if FEvent.WaitFor(FTimeOut) <> wrSignaled then

Result := 0

else GetOverlappedResult(FSocket.SocketHandle, Overlapped, DWORD(Result), False);

finally

FSocket.Unlock;

end;

end;

function TWinSocketStream.Seek(Offset: Longint; Origin: Word): Longint;

begin

Result := 0;

end;

{ TClientSocket }

constructor TClientSocket.Create(AOwner: TComponent);

begin

inherited Create(AOwner);

FClientSocket := TClientWinSocket.Create(INVALID_SOCKET);

InitSocket(FClientSocket);

end;

destructor TClientSocket.Destroy;

begin

FClientSocket.Free;

inherited Destroy;

end;

procedure TClientSocket.DoActivate(Value: Boolean);

begin

if (Value <> FClientSocket.Connected) and not (csDesigning in ComponentState) then

begin

if FClientSocket.Connected then

FClientSocket.Disconnect(FClientSocket.FSocket)

else FClientSocket.Open(FHost, FAddress, FService, FPort, ClientType = ctBlocking);

end;

end;

function TClientSocket.GetClientType: TClientType;

begin

Result := FClientSocket.ClientType;

end;

procedure TClientSocket.SetClientType(Value: TClientType);

begin

FClientSocket.ClientType := Value;

end;

{ TCustomServerSocket }

destructor TCustomServerSocket.Destroy;

begin

FServerSocket.Free;

inherited Destroy;

end;

procedure TCustomServerSocket.DoActivate(Value: Boolean);

begin

if (Value <> FServerSocket.Connected) and not (csDesigning in ComponentState) then

begin

if FServerSocket.Connected then

FServerSocket.Disconnect(FServerSocket.SocketHandle)

else FServerSocket.Listen(FHost, FAddress, FService, FPort, SOMAXCONN);

end;

end;

function TCustomServerSocket.GetServerType: TServerType;

begin

Result := FServerSocket.ServerType;

end;

procedure TCustomServerSocket.SetServerType(Value: TServerType);

begin

FServerSocket.ServerType := Value;

end;

function TCustomServerSocket.GetGetThreadEvent: TGetThreadEvent;

begin

Result := FServerSocket.OnGetThread;

end;

procedure TCustomServerSocket.SetGetThreadEvent(Value: TGetThreadEvent);

begin

FServerSocket.OnGetThread := Value;

end;

function TCustomServerSocket.GetGetSocketEvent: TGetSocketEvent;

begin

Result := FServerSocket.OnGetSocket;

end;

procedure TCustomServerSocket.SetGetSocketEvent(Value: TGetSocketEvent);

begin

FServerSocket.OnGetSocket := Value;

end;

function TCustomServerSocket.GetThreadCacheSize: Integer;

begin

Result := FServerSocket.ThreadCacheSize;

end;

procedure TCustomServerSocket.SetThreadCacheSize(Value: Integer);

begin

FServerSocket.ThreadCacheSize := Value;

end;

function TCustomServerSocket.GetOnThreadStart: TThreadNotifyEvent;

begin

Result := FServerSocket.OnThreadStart;

end;

function TCustomServerSocket.GetOnThreadEnd: TThreadNotifyEvent;

begin

Result := FServerSocket.OnThreadEnd;

end;

procedure TCustomServerSocket.SetOnThreadStart(Value: TThreadNotifyEvent);

begin

FServerSocket.OnThreadStart := Value;

end;

procedure TCustomServerSocket.SetOnThreadEnd(Value: TThreadNotifyEvent);

begin

FServerSocket.OnThreadEnd := Value;

end;

function TCustomServerSocket.GetOnClientEvent(Index: Integer): TSocketNotifyEvent;

begin

case Index of

0: Result := FServerSocket.OnClientRead;

1: Result := FServerSocket.OnClientWrite;

2: Result := FServerSocket.OnClientConnect;

3: Result := FServerSocket.OnClientDisconnect;

end;

end;

procedure TCustomServerSocket.SetOnClientEvent(Index: Integer;

Value: TSocketNotifyEvent);

begin

case Index of

0: FServerSocket.OnClientRead := Value;

1: FServerSocket.OnClientWrite := Value;

2: FServerSocket.OnClientConnect := Value;

3: FServerSocket.OnClientDisconnect := Value;

end;

end;

function TCustomServerSocket.GetOnClientError: TSocketErrorEvent;

begin

Result := FServerSocket.OnClientError;

end;

procedure TCustomServerSocket.SetOnClientError(Value: TSocketErrorEvent);

begin

FServerSocket.OnClientError := Value;

end;

{ TServerSocket }

constructor TServerSocket.Create(AOwner: TComponent);

begin

inherited Create(AOwner);

FServerSocket := TServerWinSocket.Create(INVALID_SOCKET);

InitSocket(FServerSocket);

FServerSocket.ThreadCacheSize := 10;

end;

procedure Register;

begin

RegisterComponents('JSocket', [TServerSocket,TClientSocket]);

end;

end.