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.