973 lines
28 KiB
ObjectPascal
973 lines
28 KiB
ObjectPascal
unit kolTCPSocket;
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// TTTTTTTTTT CCCCCCCC PPPPPPPPP
|
|
// T TTTT T CCCC CCCC PPPP PPPP
|
|
// TTTT CCCC PPPP PPPP
|
|
// TTTT CCCC PPPP PPPP
|
|
// TTTT CCCC PPPPPPPPP
|
|
// TTTT CCCC CCCC PPPP
|
|
// TTTT CCCCCCCC PPPP
|
|
//
|
|
// S O C K E T
|
|
//
|
|
// TCPServer, TCPClient implementation for Key Objects Library
|
|
//
|
|
// (c) 2002 by Vorobets Roman
|
|
// Roman.Vorobets@p25.f8.n454.z2.fidonet.org
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
interface
|
|
|
|
uses
|
|
kol,windows,winsock,messages;
|
|
|
|
const
|
|
WM_SOCKET=WM_USER+1;
|
|
WM_SOCKETDESTROY=WM_USER+2;
|
|
|
|
type
|
|
twndmethod=procedure(var message:tmessage) of object;
|
|
|
|
PTCPBase=^TTCPBase;
|
|
PTCPServer=^TTCPServer;
|
|
PTCPClient=^TTCPClient;
|
|
PTCPServerClient=^TTCPServerClient;
|
|
|
|
TKOLTCPClient=PTCPClient;
|
|
TKOLTCPServer=PTCPServer;
|
|
|
|
TOnTCPClientEvent = procedure(Sender: PTCPClient) of object;
|
|
TOnTCPStreamSend = TOnTCPClientEvent;
|
|
TOnTCPStreamReceive = TOnTCPClientEvent;
|
|
TOnTCPConnect = TOnTCPClientEvent;
|
|
TOnTCPManualReceive = TOnTCPClientEvent;
|
|
TOnTCPDisconnect = TOnTCPClientEvent;
|
|
|
|
TOnTCPReceive = procedure(Sender: PTCPClient; var Buf: array of byte; const Count: Integer) of object;
|
|
TOnTCPResolve = procedure(Sender: PTCPClient; const IP: String) of object;
|
|
TOnTCPAccept = function(Sender: PTCPServer; const IP: String;
|
|
const Port: SmallInt):boolean of object;
|
|
TOnTCPClientConnect = procedure(Sender: PTCPServerClient) of object;
|
|
TOnTCPError = procedure(Sender: PObj; const Error:integer) of object;
|
|
|
|
TTCPBase=object(TObj)
|
|
private
|
|
FWnd:HWnd;
|
|
FConnecting: Boolean;
|
|
function GetWnd: HWnd;
|
|
procedure Method(var message:tmessage);virtual;
|
|
procedure DoClose;
|
|
private
|
|
FPort: SmallInt;
|
|
FOnConnect: TOnTCPConnect;
|
|
FOnDisconnect: TOnTCPDisconnect;
|
|
FOnError: TOnTCPError;
|
|
FHandle: TSocket;
|
|
FConnected: Boolean;
|
|
FSection: TRTLCriticalSection;
|
|
property Wnd:HWnd read GetWnd;
|
|
function GetPort: SmallInt;
|
|
procedure SetPort(const Value: SmallInt);
|
|
procedure SetOnConnect(const Value: TOnTCPConnect);
|
|
procedure SetOnDisconnect(const Value: TOnTCPDisconnect);
|
|
procedure SetOnError(const Value: TOnTCPError);
|
|
procedure SetHandle(const Value: TSocket);
|
|
function ErrorTest(const e: integer): boolean;
|
|
protected
|
|
procedure Creating;virtual;
|
|
destructor Destroy;virtual;
|
|
public
|
|
property Connected:Boolean read FConnected;
|
|
property Online:Boolean read FConnected;
|
|
property Connecting:Boolean read FConnecting;
|
|
property Handle:TSocket read FHandle write SetHandle;
|
|
property Port:SmallInt read GetPort{FPort} write SetPort;
|
|
property OnError:TOnTCPError read FOnError write SetOnError;
|
|
property OnConnect:TOnTCPConnect read FOnConnect write SetOnConnect;
|
|
property OnDisconnect:TOnTCPDisconnect read FOnDisconnect write SetOnDisconnect;
|
|
procedure Lock;
|
|
procedure Unlock;
|
|
procedure Disconnect;virtual;
|
|
end;
|
|
|
|
TTCPServer=object(TTCPBase)
|
|
private
|
|
FConnections: PList;
|
|
FOnAccept: TOnTCPAccept;
|
|
FOnClientConnect: TOnTCPClientConnect;
|
|
FOnClientDisconnect: TOnTCPDisconnect;
|
|
FOnClientError: TOnTCPError;
|
|
FOnClientReceive: TOnTCPReceive;
|
|
FOnClientManualReceive: TOnTCPManualReceive;
|
|
FOnClientStreamReceive: TOnTCPStreamReceive;
|
|
FOnClientStreamSend: TOnTCPStreamSend;
|
|
procedure SetOnAccept(const Value: TOnTCPAccept);
|
|
procedure SetOnClientConnect(const Value: TOnTCPClientConnect);
|
|
procedure SetOnClientDisconnect(const Value: TOnTCPDisconnect);
|
|
procedure SetOnClientError(const Value: TOnTCPError);
|
|
procedure SetOnClientReceive(const Value: TOnTCPReceive);
|
|
function GetConnection(Index: Integer): PTCPServerClient;
|
|
function GetCount: Integer;
|
|
procedure Method(var message: tmessage); virtual;
|
|
procedure SetOnClientManualReceive(const Value: TOnTCPManualReceive);
|
|
procedure SetOnClientStreamReceive(const Value: TOnTCPStreamReceive);
|
|
procedure SetOnClientStreamSend(const Value: TOnTCPStreamSend);
|
|
protected
|
|
procedure Creating;virtual;
|
|
destructor Destroy;virtual;
|
|
public
|
|
property OnAccept:TOnTCPAccept read FOnAccept write SetOnAccept;
|
|
property OnClientError:TOnTCPError read FOnClientError write SetOnClientError;
|
|
property OnClientConnect:TOnTCPClientConnect read FOnClientConnect write SetOnClientConnect;
|
|
property OnClientDisconnect:TOnTCPDisconnect read FOnClientDisconnect write SetOnClientDisconnect;
|
|
property OnClientReceive:TOnTCPReceive read FOnClientReceive write SetOnClientReceive;
|
|
property OnClientManualReceive:TOnTCPManualReceive read FOnClientManualReceive write SetOnClientManualReceive;
|
|
property OnClientStreamSend:TOnTCPStreamSend read FOnClientStreamSend write SetOnClientStreamSend;
|
|
property OnClientStreamReceive:TOnTCPStreamReceive read FOnClientStreamReceive write SetOnClientStreamReceive;
|
|
property Count:Integer read GetCount;
|
|
property Connection[Index: Integer]: PTCPServerClient read GetConnection;
|
|
procedure Listen;
|
|
procedure Disconnect;virtual;
|
|
end;
|
|
|
|
TTCPClient=object(TTCPBase)
|
|
private
|
|
FHost: String;
|
|
FBuffer: array[0..4095] of byte;
|
|
FOnResolve: TOnTCPResolve;
|
|
FOnReceive: TOnTCPReceive;
|
|
FOnStreamSend: TOnTCPStreamSend;
|
|
FSendStream: PStream;
|
|
FSendAutoFree: Boolean;
|
|
FReceiveStream: PStream;
|
|
FReceiveAutoFree: Boolean;
|
|
FReceiveAutoFreeSize: Integer;
|
|
FReceiveStartPos: Integer;
|
|
FOnManualReceive: TOnTCPManualReceive;
|
|
FOnStreamReceive: TOnTCPStreamReceive;
|
|
FIndex: Integer;
|
|
procedure SetHost(const Value: String);
|
|
procedure SetOnResolve(const Value: TOnTCPResolve);
|
|
procedure SetOnReceive(const Value: TOnTCPReceive);
|
|
procedure SetOnStreamSend(const Value: TOnTCPStreamSend);
|
|
procedure Method(var message:tmessage);virtual;
|
|
function SendStreamPiece: Boolean;
|
|
procedure SetOnManualReceive(const Value: TOnTCPManualReceive);
|
|
procedure SetOnStreamReceive(const Value: TOnTCPStreamReceive);
|
|
procedure SetIndex(const Value: Integer);virtual;
|
|
protected
|
|
destructor Destroy;virtual;
|
|
public
|
|
property OnReceive:TOnTCPReceive read FOnReceive write SetOnReceive;
|
|
property OnManualReceive:TOnTCPManualReceive read FOnManualReceive write SetOnManualReceive;
|
|
property OnResolve:TOnTCPResolve read FOnResolve write SetOnResolve;
|
|
property OnStreamSend:TOnTCPStreamSend read FOnStreamSend write SetOnStreamSend;
|
|
property OnStreamReceive:TOnTCPStreamReceive read FOnStreamReceive write SetOnStreamReceive;
|
|
property Host:String read FHost write SetHost;
|
|
property Index:Integer read FIndex write SetIndex;
|
|
function StreamSending:Boolean;
|
|
function StreamReceiving:Boolean;
|
|
procedure Connect;virtual;
|
|
function Send(var Buf; const Count: Integer): Integer;
|
|
procedure SendString(S: String);
|
|
function SendStream(Stream: PStream; const AutoFree: Boolean): Boolean;
|
|
procedure SetReceiveStream(Stream: PStream; const AutoFree: Boolean=false;
|
|
const AutoFreeSize: Integer=0);
|
|
function ReceiveLength: Integer;
|
|
function ReceiveBuf(var Buf; Count: Integer): Integer;
|
|
end;
|
|
|
|
TTCPServerClient=object(TTCPClient)
|
|
private
|
|
FIP: String;
|
|
FServer: PTCPServer;
|
|
procedure SetIndex(const Value: Integer);virtual;
|
|
public
|
|
property IP: String read FIP;
|
|
procedure Connect;virtual;
|
|
procedure Disconnect;virtual;
|
|
end;
|
|
|
|
function NewTCPServer: PTCPServer;
|
|
function NewTCPClient: PTCPClient;
|
|
function Err2Str(const id: integer): string;
|
|
function TCPGetHostByName(name: pchar): string;
|
|
|
|
procedure Startup;
|
|
procedure Cleanup;
|
|
|
|
implementation
|
|
|
|
type
|
|
pobjectinstance=^tobjectinstance;
|
|
tobjectinstance=packed record
|
|
code:byte;
|
|
offset:integer;
|
|
case integer of
|
|
0:(next:pobjectinstance);
|
|
1:(method:twndmethod);
|
|
end;
|
|
|
|
pinstanceblock=^tinstanceblock;
|
|
tinstanceblock=packed record
|
|
next:pinstanceblock;
|
|
code:array[1..2] of byte;
|
|
wndprocptr:pointer;
|
|
instances: array[0..$ff] of tobjectinstance;
|
|
end;
|
|
|
|
var
|
|
instblocklist:pinstanceblock;
|
|
instfreelist:pobjectinstance;
|
|
|
|
wsadata:twsadata;
|
|
|
|
function NewTCPServerClient(Server: PTCPServer): PTCPServerClient;forward;
|
|
|
|
function stdwndproc(window:hwnd;message:dword;wparam:WPARAM;
|
|
lparam:LPARAM):LRESULT;stdcall;assembler;
|
|
asm
|
|
XOR EAX,EAX
|
|
PUSH EAX
|
|
PUSH LParam
|
|
PUSH WParam
|
|
PUSH Message
|
|
MOV EDX,ESP
|
|
MOV EAX,[ECX].Longint[4]
|
|
CALL [ECX].Pointer
|
|
ADD ESP,12
|
|
POP EAX
|
|
end;
|
|
|
|
function calcjmpoffset(src,dest:pointer):longint;
|
|
begin
|
|
result:=longint(dest)-(longint(src)+5);
|
|
end;
|
|
|
|
function MakeObjectInstance(Method: TWndMethod): Pointer;
|
|
const
|
|
blockcode:array[1..2] of byte=($59,$E9);
|
|
pagesize=4096;
|
|
var
|
|
block:pinstanceblock;
|
|
instance:pobjectinstance;
|
|
begin
|
|
if instfreelist=nil then
|
|
begin
|
|
block:=virtualalloc(nil,PageSize, MEM_COMMIT,PAGE_EXECUTE_READWRITE);
|
|
block^.next:=instblocklist;
|
|
move(blockcode,block^.code,sizeof(blockcode));
|
|
block^.wndprocptr:=pointer(calcjmpoffset(@block^.code[2],@stdwndproc));
|
|
instance:=@block^.instances;
|
|
repeat
|
|
instance^.code:=$E8;
|
|
instance^.offset:=calcjmpoffset(instance,@block^.code);
|
|
instance^.next:=instfreelist;
|
|
instfreelist:=instance;
|
|
inc(longint(instance),sizeof(tobjectinstance));
|
|
until longint(instance)-longint(block)>=sizeof(tinstanceblock);
|
|
instblocklist:=block;
|
|
end;
|
|
result:=instfreelist;
|
|
instance:=instfreelist;
|
|
instfreelist:=instance^.next;
|
|
instance^.method:=method;
|
|
end;
|
|
|
|
procedure FreeObjectInstance(ObjectInstance: Pointer);
|
|
begin
|
|
if objectinstance<>nil then
|
|
begin
|
|
pobjectinstance(objectinstance)^.next:=instfreelist;
|
|
instfreelist:=objectinstance;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
utilclass:twndclass=(lpfnwndproc:@defwindowproc;lpszclassname:'TCPSocket');
|
|
|
|
function AllocateHWnd(Method: TWndMethod): HWND;
|
|
var
|
|
tempclass:twndclass;
|
|
classregistered:boolean;
|
|
begin
|
|
utilclass.hinstance:=hinstance;
|
|
classregistered:=getclassinfo(hinstance,utilclass.lpszclassname,tempclass);
|
|
if not classregistered or (tempclass.lpfnwndproc<>@defwindowproc) then
|
|
begin
|
|
if classregistered then unregisterclass(utilclass.lpszclassname,hinstance);
|
|
registerclass(utilclass);
|
|
end;
|
|
result:=createwindowex(WS_EX_TOOLWINDOW,utilclass.lpszclassname,nil,
|
|
WS_POPUP,0,0,0,0,0,0,hinstance,nil);
|
|
if assigned(method) then setwindowlong(result,GWL_WNDPROC,longint(makeobjectinstance(method)));
|
|
end;
|
|
|
|
procedure DeallocateHWnd(Wnd: HWND);
|
|
var
|
|
instance:pointer;
|
|
begin
|
|
instance:=pointer(getwindowlong(wnd,GWL_WNDPROC));
|
|
destroywindow(wnd);
|
|
if instance<>@defwindowproc then freeobjectinstance(instance);
|
|
end;
|
|
|
|
procedure Startup;
|
|
begin
|
|
if bool(wsastartup($101,wsadata)) then showmessage('WSAStartup error.');
|
|
end;
|
|
|
|
procedure Cleanup;
|
|
begin
|
|
if bool(wsacleanup) then showmessage('WSACleanup error');
|
|
end;
|
|
|
|
{ TTCPBase }
|
|
|
|
procedure TTCPBase.Creating;
|
|
begin
|
|
startup;
|
|
initializecriticalsection(fsection);
|
|
fhandle:=SOCKET_ERROR;
|
|
end;
|
|
|
|
destructor TTCPBase.Destroy;
|
|
begin
|
|
if fwnd<>0 then deallocatehwnd(fwnd);
|
|
doclose;
|
|
disconnect;
|
|
deletecriticalsection(fsection);
|
|
cleanup;
|
|
end;
|
|
|
|
procedure TTCPBase.Disconnect;
|
|
begin
|
|
if fhandle<>SOCKET_ERROR then
|
|
begin
|
|
doclose;
|
|
if fconnected then
|
|
begin
|
|
fconnected:=false;
|
|
if assigned(ondisconnect) then ondisconnect(@self);
|
|
end;
|
|
fconnecting:=false;
|
|
end;
|
|
end;
|
|
|
|
procedure TTCPBase.DoClose;
|
|
begin
|
|
if fhandle<>SOCKET_ERROR then
|
|
begin
|
|
errortest(closesocket(fhandle));
|
|
fhandle:=SOCKET_ERROR;
|
|
end;
|
|
end;
|
|
|
|
function TTCPBase.ErrorTest(const e: integer): boolean;
|
|
var
|
|
wsae: Integer;
|
|
begin
|
|
{ msgok(int2str(e));
|
|
msgok(int2str(SOCKET_ERROR));
|
|
msgok(int2str(INVALID_SOCKET)); }
|
|
|
|
result:= (e = SOCKET_ERROR) or (e = INVALID_SOCKET);
|
|
if result then begin
|
|
wsae:=wsagetlasterror;
|
|
if wsae<>WSAEWOULDBLOCK then
|
|
begin
|
|
if assigned(onerror) then onerror(@self,wsae) else
|
|
showmessage('Socket error '+err2str(wsae)+' on socket '+int2str(fhandle));
|
|
end else result:=false;
|
|
end;
|
|
end;
|
|
|
|
function TTCPBase.GetWnd: HWnd;
|
|
begin
|
|
if fwnd=0 then fwnd:=allocatehwnd(method);
|
|
result:=fwnd;
|
|
end;
|
|
|
|
procedure TTCPBase.Lock;
|
|
begin
|
|
entercriticalsection(fsection);
|
|
end;
|
|
|
|
procedure TTCPBase.Method(var message: tmessage);
|
|
begin
|
|
if message.msg<>WM_SOCKET then exit;
|
|
if message.lparamhi>WSABASEERR then
|
|
begin
|
|
wsasetlasterror(message.lparamhi);
|
|
errortest(SOCKET_ERROR);
|
|
if fconnecting then doclose;
|
|
fconnecting:=false;
|
|
end;
|
|
case message.lparamlo of
|
|
FD_CLOSE:begin
|
|
fconnected:=false;
|
|
fconnecting:=false;
|
|
if assigned(ondisconnect) then ondisconnect(@self);
|
|
if fhandle<>SOCKET_ERROR then doclose;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TTCPBase.SetHandle(const Value: TSocket);
|
|
begin
|
|
FHandle := Value;
|
|
end;
|
|
|
|
procedure TTCPBase.SetOnDisconnect(const Value: TOnTCPDisconnect);
|
|
begin
|
|
FOnDisconnect := Value;
|
|
end;
|
|
|
|
procedure TTCPBase.SetOnError(const Value: TOnTCPError);
|
|
begin
|
|
FOnError := Value;
|
|
end;
|
|
|
|
procedure TTCPBase.SetPort(const Value: SmallInt);
|
|
begin
|
|
FPort := Value;
|
|
end;
|
|
|
|
function TTCPBase.GetPort: SmallInt;
|
|
var buf: sockaddr_in; bufSz: Integer;
|
|
begin
|
|
if FConnected then
|
|
begin
|
|
bufSz := SizeOf(buf);
|
|
ZeroMemory( @buf, bufSz );
|
|
getsockname(fhandle, buf, bufSz);
|
|
FPort := htons(buf.sin_port);
|
|
end;
|
|
Result := FPort;
|
|
end;
|
|
|
|
function NewTCPServer: PTCPServer;
|
|
begin
|
|
new(result,create);
|
|
result.creating;
|
|
end;
|
|
|
|
function NewTCPClient: PTCPClient;
|
|
begin
|
|
new(result,create);
|
|
result.creating;
|
|
end;
|
|
|
|
function NewTCPServerClient(Server: PTCPServer): PTCPServerClient;
|
|
begin
|
|
new(result,create);
|
|
result.creating;
|
|
result.fserver:=server;
|
|
end;
|
|
|
|
procedure TTCPBase.Unlock;
|
|
begin
|
|
leavecriticalsection(fsection);
|
|
end;
|
|
|
|
{ TTCPClient }
|
|
|
|
procedure TTCPClient.Connect;
|
|
var
|
|
adr: TSockAddr;
|
|
begin
|
|
disconnect;
|
|
fhandle:= socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
|
if not errortest(fhandle) then begin
|
|
WSAAsyncSelect(fhandle, wnd, WM_SOCKET, FD_READ or FD_WRITE or FD_CONNECT or FD_CLOSE);
|
|
with adr do begin
|
|
sin_family:= AF_INET;
|
|
sin_port:= htons(port);
|
|
//Integer(sin_addr):= inet_addr(PChar(host));
|
|
sin_addr.S_addr:= inet_addr(PChar(host));
|
|
|
|
if Integer(sin_addr) = SOCKET_ERROR then begin
|
|
sin_addr.S_addr:= PInAddr(gethostbyname(PChar(Host)).h_addr_list^)^.S_addr;
|
|
end;
|
|
|
|
//msgok('bly' + int2str(sin_addr.S_addr));
|
|
{if Integer(sin_addr) = SOCKET_ERROR then begin
|
|
// must be WSAAsyncGetHostByName
|
|
ph:= winsock.gethostbyname(pchar(host));
|
|
if ph=nil then showmessage('gethostbyname() error');
|
|
move(ph.h_addr^^,sin_addr,ph.h_length);
|
|
if assigned(onresolve) then onresolve(@self,inet_ntoa(adr.sin_addr));
|
|
end;}
|
|
|
|
end;
|
|
fconnecting:= not errortest(Integer(adr.sin_addr)) and not errortest(WinSock.connect(fhandle, adr, SizeOf(adr)));
|
|
if not fconnecting then doclose;
|
|
end;
|
|
end;
|
|
|
|
destructor TTCPClient.Destroy;
|
|
begin
|
|
if fsendautofree and (fsendstream<>nil) then fsendstream.free;
|
|
fsendstream:=nil;
|
|
inherited;
|
|
end;
|
|
|
|
function TTCPClient.StreamReceiving: Boolean;
|
|
begin
|
|
Result:= Assigned(FReceiveStream);
|
|
end;
|
|
|
|
function TTCPClient.StreamSending: Boolean;
|
|
begin
|
|
Result:= Bool(fsendstream);
|
|
end;
|
|
|
|
procedure TTCPClient.Method(var message: tmessage);
|
|
var
|
|
sz:integer;
|
|
begin
|
|
inherited;
|
|
if (message.msg<>WM_SOCKET) then exit;
|
|
if message.lparamhi>WSABASEERR then
|
|
begin
|
|
if message.lparamlo=FD_CLOSE then
|
|
begin
|
|
if streamsending then
|
|
begin
|
|
if fsendautofree then fsendstream.free;
|
|
if assigned(onstreamsend) then onstreamsend(@self);
|
|
end;
|
|
if streamreceiving then
|
|
begin
|
|
if freceiveautofree then freceivestream.free;
|
|
if assigned(onstreamreceive) then onstreamreceive(@self);
|
|
end;
|
|
end;
|
|
end else
|
|
case message.lparamlo of
|
|
FD_CONNECT:begin
|
|
fconnected:=true;
|
|
fconnecting:=false;
|
|
if assigned(onconnect) then onconnect(@self);
|
|
end;
|
|
FD_READ:if (freceivestream=nil) and assigned(onmanualreceive) then onmanualreceive(@self) else
|
|
begin
|
|
lock;
|
|
// repeat
|
|
ioctlsocket(fhandle,FIONREAD,sz);
|
|
if sz>0 then
|
|
begin
|
|
if sz>sizeof(fbuffer) then sz:=sizeof(fbuffer);
|
|
sz:=receivebuf(fbuffer,sz);
|
|
errortest(sz);
|
|
if freceivestream<>nil then
|
|
begin
|
|
freceivestream.write(fbuffer,sz);
|
|
if assigned(onstreamreceive) then onstreamreceive(@self);
|
|
end else if assigned(onreceive) then onreceive(@self,fbuffer,sz);
|
|
end;
|
|
// until (sz<=0) or //not fmaxsendstreamspeed or
|
|
// ((freceivestream<>nil) and freceiveautofree and
|
|
// (freceivestream.size>=freceiveautofreesize));
|
|
unlock;
|
|
if (freceivestream<>nil) and freceiveautofree and
|
|
(integer(freceivestream.position)+freceivestartpos>=freceiveautofreesize) then
|
|
begin
|
|
freceivestream.free;
|
|
freceivestream:=nil;
|
|
if assigned(onstreamreceive) then onstreamreceive(@self);
|
|
end;
|
|
end;
|
|
FD_WRITE:if streamsending then sendstreampiece;// else if assigned(onwrite) then onwrite(@self);
|
|
end;
|
|
end;
|
|
|
|
function TTCPClient.ReceiveBuf(var Buf; Count: Integer): Integer;
|
|
begin
|
|
result:=0;
|
|
if not fconnected or (fhandle=SOCKET_ERROR) or (count<=0) then exit;
|
|
lock;
|
|
result:=recv(fhandle,buf,count,0);
|
|
errortest(result);
|
|
unlock;
|
|
end;
|
|
|
|
function TTCPClient.ReceiveLength: Integer;
|
|
begin
|
|
ioctlsocket(fhandle,FIONREAD,result);
|
|
end;
|
|
|
|
function TTCPClient.Send(var Buf; const Count: Integer): Integer;
|
|
begin
|
|
result:=winsock.send(fhandle,buf,count,0);
|
|
end;
|
|
|
|
function TTCPClient.SendStream(Stream: PStream; const AutoFree: Boolean): Boolean;
|
|
begin
|
|
result:=false;
|
|
if fsendstream=nil then
|
|
begin
|
|
fsendstream:=stream;
|
|
fsendautofree:=autofree;
|
|
result:=sendstreampiece;
|
|
end;
|
|
end;
|
|
|
|
function TTCPClient.SendStreamPiece: Boolean;
|
|
var
|
|
buf:array[0..4095] of byte;
|
|
startpos,amountinbuf,amountsent:integer;
|
|
begin
|
|
result:=false;
|
|
if not fconnected or (fhandle=SOCKET_ERROR) or (fsendstream=nil) then exit;
|
|
lock;
|
|
repeat
|
|
startpos:=fsendstream.position;
|
|
amountinbuf:=fsendstream.read(buf,sizeof(buf));
|
|
if amountinbuf>0 then
|
|
begin
|
|
amountsent:=send(buf,amountinbuf);
|
|
if amountsent=SOCKET_ERROR then
|
|
begin
|
|
if errortest(SOCKET_ERROR) then
|
|
begin
|
|
fsendstream:=nil;
|
|
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
|
|
if fsendautofree then fsendstream.free;
|
|
fsendstream:=nil;
|
|
break;
|
|
end;
|
|
end else
|
|
begin
|
|
fsendstream:=nil;
|
|
break;
|
|
end;
|
|
until false;
|
|
result:=true;
|
|
unlock;
|
|
if assigned(onstreamsend) then onstreamsend(@self);
|
|
end;
|
|
|
|
procedure TTCPClient.SendString(S: String);
|
|
begin
|
|
send(s[1], length(s));
|
|
end;
|
|
|
|
procedure TTCPClient.SetHost(const Value: String);
|
|
begin
|
|
FHost := Value;
|
|
end;
|
|
|
|
procedure TTCPClient.SetIndex(const Value: Integer);
|
|
begin
|
|
FIndex := Value;
|
|
end;
|
|
|
|
procedure TTCPBase.SetOnConnect(const Value: TOnTCPConnect);
|
|
begin
|
|
FOnConnect := Value;
|
|
end;
|
|
|
|
procedure TTCPClient.SetOnManualReceive(const Value: TOnTCPManualReceive);
|
|
begin
|
|
FOnManualReceive := Value;
|
|
end;
|
|
|
|
procedure TTCPClient.SetOnReceive(const Value: TOnTCPReceive);
|
|
begin
|
|
FOnReceive := Value;
|
|
end;
|
|
|
|
procedure TTCPClient.SetOnResolve(const Value: TOnTCPResolve);
|
|
begin
|
|
FOnResolve := Value;
|
|
end;
|
|
|
|
procedure TTCPClient.SetOnStreamReceive(const Value: TOnTCPStreamReceive);
|
|
begin
|
|
FOnStreamReceive := Value;
|
|
end;
|
|
|
|
procedure TTCPClient.SetOnStreamSend(const Value: TOnTCPStreamSend);
|
|
begin
|
|
FOnStreamSend := Value;
|
|
end;
|
|
|
|
procedure TTCPClient.SetReceiveStream(Stream: PStream; const AutoFree: Boolean = False; const AutoFreeSize: Integer=0);
|
|
begin
|
|
if Autofree and (AutoFreeSize = 0) then Exit;
|
|
if Assigned(FReceiveStream) then FReceiveStream.free;
|
|
FReceiveAutoFree:= AutoFree;
|
|
FReceiveAutoFreeSize:= AutoFreeSize;
|
|
FReceiveStartpos:= Stream.Position;
|
|
FReceiveStream:= Stream;
|
|
end;
|
|
|
|
{ TTCPServer }
|
|
|
|
procedure TTCPServer.Creating;
|
|
begin
|
|
inherited;
|
|
fconnections:=newlist;
|
|
end;
|
|
|
|
destructor TTCPServer.Destroy;
|
|
var
|
|
i:integer;
|
|
begin
|
|
for i:=0 to pred(count) do connection[i].free;
|
|
fconnections.free;
|
|
fconnections:=nil;
|
|
inherited;
|
|
end;
|
|
|
|
procedure TTCPServer.Disconnect;
|
|
begin
|
|
if fconnections=nil then exit;
|
|
lock;
|
|
while count>0 do connection[0].disconnect;
|
|
unlock;
|
|
inherited;
|
|
end;
|
|
|
|
function TTCPServer.GetConnection(Index: Integer): PTCPServerClient;
|
|
begin
|
|
result:=ptcpserverclient(fconnections.items[index]);
|
|
end;
|
|
|
|
function TTCPServer.GetCount: Integer;
|
|
begin
|
|
result:=fconnections.count;
|
|
end;
|
|
|
|
procedure TTCPServer.Listen;
|
|
var
|
|
adr:tsockaddr;
|
|
begin
|
|
if fhandle<>SOCKET_ERROR then exit;
|
|
fhandle:=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
|
|
if not errortest(fhandle) then
|
|
begin
|
|
with adr do
|
|
begin
|
|
sin_family:=AF_INET;
|
|
sin_port:=htons(port);
|
|
integer(sin_addr):=INADDR_ANY;
|
|
end;
|
|
if errortest(bind(fhandle,adr,sizeof(adr))) then doclose else
|
|
begin
|
|
wsaasyncselect(fhandle,wnd,WM_SOCKET,FD_ACCEPT or FD_CLOSE or FD_CONNECT);
|
|
if errortest(winsock.listen(fhandle,64)) then
|
|
doclose
|
|
else
|
|
begin
|
|
FConnected := True;
|
|
if assigned(onconnect) then onconnect(@self);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TTCPServer.Method(var message: tmessage);
|
|
var
|
|
adr:tsockaddr;
|
|
sz:integer;
|
|
sock:TSocket;
|
|
sclient:ptcpserverclient;
|
|
begin
|
|
inherited;
|
|
case message.msg of
|
|
WM_SOCKET:
|
|
if message.lparamhi<=WSABASEERR then
|
|
case message.lparamlo of
|
|
FD_ACCEPT:begin
|
|
sz:=sizeof(adr);
|
|
sock:=accept(fhandle,@adr,@sz);
|
|
if not errortest(sock) then
|
|
begin
|
|
if not assigned(onaccept) or onaccept(@self,inet_ntoa(adr.sin_addr),htons(adr.sin_port)) then
|
|
begin
|
|
sclient:=newtcpserverclient(@self);
|
|
with sclient^ do
|
|
begin
|
|
wsaasyncselect(sock,wnd,WM_SOCKET,FD_READ or FD_WRITE or FD_CLOSE);
|
|
fhost:=inet_ntoa(adr.sin_addr);
|
|
fport:=htons(adr.sin_port);
|
|
fip:=fhost;
|
|
fhandle:=sock;
|
|
fconnected:=true;
|
|
fconnecting:=false;
|
|
findex:=fconnections.count;
|
|
onerror:=onclienterror;
|
|
ondisconnect:=onclientdisconnect;
|
|
onreceive:=onclientreceive;
|
|
onmanualreceive:=onclientmanualreceive;
|
|
onstreamsend:=onclientstreamsend;
|
|
onstreamreceive:=onclientstreamreceive;
|
|
end;
|
|
fconnections.add(sclient);
|
|
if assigned(onclientconnect) then onclientconnect(sclient);
|
|
end else closesocket(sock);
|
|
end;
|
|
end;
|
|
end;
|
|
WM_SOCKETDESTROY:ptcpserverclient(message.wparam).free;
|
|
end;
|
|
end;
|
|
|
|
procedure TTCPServer.SetOnAccept(const Value: TOnTCPAccept);
|
|
begin
|
|
FOnAccept := Value;
|
|
end;
|
|
|
|
procedure TTCPServer.SetOnClientConnect(const Value: TOnTCPClientConnect);
|
|
begin
|
|
FOnClientConnect := Value;
|
|
end;
|
|
|
|
procedure TTCPServer.SetOnClientDisconnect(const Value: TOnTCPDisconnect);
|
|
begin
|
|
FOnClientDisconnect := Value;
|
|
end;
|
|
|
|
procedure TTCPServer.SetOnClientError(const Value: TOnTCPError);
|
|
begin
|
|
FOnClientError := Value;
|
|
end;
|
|
|
|
procedure TTCPServer.SetOnClientManualReceive( const Value: TOnTCPManualReceive);
|
|
begin
|
|
FOnClientManualReceive := Value;
|
|
end;
|
|
|
|
procedure TTCPServer.SetOnClientReceive(const Value: TOnTCPReceive);
|
|
begin
|
|
FOnClientReceive := Value;
|
|
end;
|
|
|
|
function Err2Str(const id: integer): string;
|
|
begin
|
|
case id of
|
|
WSAEINTR:result:='WSAEINTR';
|
|
WSAEBADF:result:='WSAEBADF';
|
|
WSAEACCES:result:='WSAEACCES';
|
|
WSAEFAULT:result:='WSAEFAULT';
|
|
WSAEINVAL:result:='WSAEINVAL';
|
|
WSAEMFILE:result:='WSAEMFILE';
|
|
WSAEWOULDBLOCK:result:='WSAEWOULDBLOCK';
|
|
WSAEINPROGRESS:result:='WSAEINPROGRESS';
|
|
WSAEALREADY:result:='WSAEALREADY';
|
|
WSAENOTSOCK:result:='WSAENOTSOCK';
|
|
WSAEDESTADDRREQ:result:='WSAEDESTADDRREQ';
|
|
WSAEMSGSIZE:result:='WSAEMSGSIZE';
|
|
WSAEPROTOTYPE:result:='WSAEPROTOTYPE';
|
|
WSAENOPROTOOPT:result:='WSAENOPROTOOPT';
|
|
WSAEPROTONOSUPPORT:result:='WSAEPROTONOSUPPORT';
|
|
WSAESOCKTNOSUPPORT:result:='WSAESOCKTNOSUPPORT';
|
|
WSAEOPNOTSUPP:result:='WSAEOPNOTSUPP';
|
|
WSAEPFNOSUPPORT:result:='WSAEPFNOSUPPORT';
|
|
WSAEAFNOSUPPORT:result:='WSAEAFNOSUPPORT';
|
|
WSAEADDRINUSE:result:='WSAEADDRINUSE';
|
|
WSAEADDRNOTAVAIL:result:='WSAEADDRNOTAVAIL';
|
|
WSAENETDOWN:result:='WSAENETDOWN';
|
|
WSAENETUNREACH:result:='WSAENETUNREACH';
|
|
WSAENETRESET:result:='WSAENETRESET';
|
|
WSAECONNABORTED:result:='WSAECONNABORTED';
|
|
WSAECONNRESET:result:='WSAECONNRESET';
|
|
WSAENOBUFS:result:='WSAENOBUFS';
|
|
WSAEISCONN:result:='WSAEISCONN';
|
|
WSAENOTCONN:result:='WSAENOTCONN';
|
|
WSAESHUTDOWN:result:='WSAESHUTDOWN';
|
|
WSAETOOMANYREFS:result:='WSAETOOMANYREFS';
|
|
WSAETIMEDOUT:result:='WSAETIMEDOUT';
|
|
WSAECONNREFUSED:result:='WSAECONNREFUSED';
|
|
WSAELOOP:result:='WSAELOOP';
|
|
WSAENAMETOOLONG:result:='WSAENAMETOOLONG';
|
|
WSAEHOSTDOWN:result:='WSAEHOSTDOWN';
|
|
WSAEHOSTUNREACH:result:='WSAEHOSTUNREACH';
|
|
WSAENOTEMPTY:result:='WSAENOTEMPTY';
|
|
WSAEPROCLIM:result:='WSAEPROCLIM';
|
|
WSAEUSERS:result:='WSAEUSERS';
|
|
WSAEDQUOT:result:='WSAEDQUOT';
|
|
WSAESTALE:result:='WSAESTALE';
|
|
WSAEREMOTE:result:='WSAEREMOTE';
|
|
WSASYSNOTREADY:result:='WSASYSNOTREADY';
|
|
WSAVERNOTSUPPORTED:result:='WSAVERNOTSUPPORTED';
|
|
WSANOTINITIALISED:result:='WSANOTINITIALISED';
|
|
WSAHOST_NOT_FOUND:result:='WSAHOST_NOT_FOUND';
|
|
WSATRY_AGAIN:result:='WSATRY_AGAIN';
|
|
WSANO_RECOVERY:result:='WSANO_RECOVERY';
|
|
WSANO_DATA:result:='WSANO_DATA';
|
|
else result:='WSAEUNKNOWN';
|
|
end;
|
|
end;
|
|
|
|
procedure TTCPServer.SetOnClientStreamReceive( const Value: TOnTCPStreamReceive);
|
|
begin
|
|
FOnClientStreamReceive := Value;
|
|
end;
|
|
|
|
procedure TTCPServer.SetOnClientStreamSend(const Value: TOnTCPStreamSend);
|
|
begin
|
|
FOnClientStreamSend := Value;
|
|
end;
|
|
|
|
{ TTCPServerClient }
|
|
|
|
procedure TTCPServerClient.Connect;
|
|
begin
|
|
showmessage('Can''t connect ServerClient');
|
|
end;
|
|
|
|
procedure TTCPServerClient.Disconnect;
|
|
var
|
|
i,j:integer;
|
|
srv:ptcpserver;
|
|
begin
|
|
if fserver<>nil then
|
|
begin
|
|
srv:=fserver;
|
|
fserver:=nil;
|
|
srv.lock;
|
|
i:=srv.fconnections.indexof(@self);
|
|
for j:=pred(srv.fconnections.count) downto succ(i) do dec(srv.connection[j].findex);
|
|
srv.fconnections.delete(i);
|
|
srv.unlock;
|
|
postmessage(srv.wnd,WM_SOCKETDESTROY,integer(@self),0);
|
|
end;
|
|
inherited;
|
|
end;
|
|
|
|
function TCPGetHostByName(name: pchar): string;
|
|
var
|
|
host:phostent;
|
|
adr:in_addr;
|
|
begin
|
|
host:=gethostbyname(name);
|
|
move(host.h_addr^^,adr,host.h_length);
|
|
result:=inet_ntoa(adr);
|
|
end;
|
|
|
|
procedure TTCPServerClient.SetIndex(const Value: Integer);
|
|
begin
|
|
showmessage('Can''t set index of ServerClient');
|
|
end;
|
|
|
|
initialization
|
|
instblocklist:=nil;
|
|
instfreelist:=nil;
|
|
|
|
end.
|