Sources
Delphi Russian Knowledge Base
DRKB - это самая большая и удобная в использовании база знаний по Дельфи в рунете, составленная Виталием Невзоровым

Crtsock.pas

01.01.2007
unit crtsock;
 
{
  CrtSocket for Delphi 32
  Copyright (C) 1999-2001  Paul Toth <tothpaul@free.fr>
  http://tothpaul.free.fr
 
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
}
 
interface
 
uses windows,sysutils;
 
{-$define debug}
 
// Server side :
//  - start a server
//  - wait for a client
function StartServer(Port:word):integer;
function WaitClient(Server:integer):integer;
function WaitClientEx(Server:integer; var ip:string):integer;
 
// Client side :
//  - call a server
function CallServer(Server:string;Port:word):integer;
 
// Both side :
//  - Assign CRT Sockets
//  - Disconnect server
procedure AssignCrtSock(Socket:integer; Var Input,Output:TextFile);
procedure Disconnect(Socket:integer);
 
// BroadCasting (UDP)
function StartBroadCast(Port:word):integer;
function SendBroadCast(Server:integer; Port:word; s:string):integer;
function SendBroadCastTo(Server:integer; Port:word; ip,s:string):integer;
function ReadBroadCast(Server:integer; Port:word):string;
function ReadBroadCastEx(Server:integer; Port:word; var ip:string):string;
 
// BlockRead
function SockAvail(Socket:integer):integer;
function DataAvail(Var F:TextFile):integer;
Function BlockReadsock(Var F:TextFile; var s:string):boolean;
 
Function send(socket:integer; data:pointer; datalen,flags:integer):integer; stdcall; far;
Function recv(socket:integer; data:pchar; datalen,flags:integer):integer; stdcall; far; 
 
// some usefull SOCKET apis
type
  PHost = ^THost;
  THost = packed record
    name     : PChar;
    aliases  : ^PChar;
    addrtype : Smallint;
    length   : Smallint;
    addr     : ^pointer;
  end;
 
  TSockAddr=packed record
   Family:word;
   Port:word;
   Addr:LongInt;
   Zeros:array[0..7] of byte;
  end;
 
  TTimeOut=packed record
   sec:integer;
   usec:integer;
  end;
 
Const
 fIoNbRead = $4004667F;
 
Function socket(Family,Kind,Protocol:integer):integer; stdcall;
Function closesocket(socket:Integer):integer; stdcall;
Function gethostbyname(HostName:PChar):PHost; stdcall;
Function gethostname(name:pchar; size:integer):integer; stdcall;
Function bind(Socket:Integer; Var SockAddr:TSockAddr; AddrLen:integer):integer; stdcall;
Function WSAGetLastError:integer; stdcall;
Function ioctlsocket(socket:integer; cmd: integer; var arg: integer): Integer; stdcall;
 
// Convert an IP Value to xxx.xxx.xxx.xxx string
Function LongToIp(Long:LongInt):string;
Function IpToLong(ip:string):longint;
Function HostToLong(AHost:string):LongInt;
 
Var
 EofSock:boolean;
 
implementation
 
//------ winsock -------------------------------------------------------
Const
 WinSock='wsock32.dll'; { 32bits socket DLL }
 Internet=2; { Internat familly }
 Stream=1;   { Streamed socket }
 Datagrams=2;
// fIoNbRead = $4004667F;
 sol_socket=$FFFF;
 SO_BROADCAST    = $0020;          { permit sending of broadcast msgs }
 
Type
  TWSAData = packed record
    wVersion: Word;
    wHighVersion: Word;
    szDescription: array[0..256] of Char;
    szSystemStatus: array[0..128] of Char;
    iMaxSockets: Word;
    iMaxUdpDg: Word;
    lpVendorInfo: PChar;
  end;
 
{ Winsock }
Function WSAStartup(Version:word; Var Data:TwsaData):integer; stdcall; far; external winsock;
Function socket(Family,Kind,Protocol:integer):integer; stdcall; far; external winsock;
Function shutdown(Socket,How:Integer):integer; stdcall; far; external winsock;
Function closesocket(socket:Integer):integer; stdcall; far; external winsock;
Function WSACleanup:integer; stdcall; far; external winsock;
//Function WSAAsyncSelect(Socket:Integer; Handle:Hwnd; Msg:word; Level:Longint):longint; stdcall; far; external winsock;
Function bind(Socket:Integer; Var SockAddr:TSockAddr; AddrLen:integer):integer; stdcall; far; external winsock;
Function listen(socket,flags:Integer):integer; stdcall; far; external winsock;
Function connect(socket:Integer; Var SockAddr:TSockAddr; AddrLen:integer):integer; stdcall; far; external winsock;
Function accept(socket:Integer; Var SockAddr:TSockAddr; Var AddrLen:Integer):integer; stdcall; far; external winsock;
Function WSAGetLastError:integer; stdcall; far; external winsock;
Function recv(socket:integer; data:pchar; datalen,flags:integer):integer; stdcall; far; external winsock;
Function send(socket:integer; data:pointer; datalen,flags:integer):integer; stdcall; far; external winsock;
//Function getpeername(socket:integer; var SockAddr:TSockAddr; Var AddrLen:Integer):Integer; stdcall; far; external winsock;
Function gethostbyname(HostName:PChar):PHost; stdcall; far; external winsock;
//Function getsockname(socket:integer; var SockAddr:TSockAddr; Var AddrLen:Integer):integer; stdcall; far; external winsock;
//Function inet_ntoa(addr:longint):PChar; stdcall; far; external winsock;
Function WSAIsBlocking:boolean; stdcall; far; external winsock;
Function WSACancelBlockingCall:integer; stdcall; far; external winsock;
Function ioctlsocket(socket:integer; cmd: integer; var arg: integer): Integer; stdcall; far; external winsock;
//Function gethostbyaddr(var addr:longint; size,atype:integer):PHost; stdcall; far; external winsock;
Function gethostname(name:pchar; size:integer):integer; stdcall; far; external winsock;
function select(nfds:integer; readfds, writefds, exceptfds:pointer; var timeout:TTimeOut):integer; stdcall; far; external winsock;
function setsockopt(socket,level,optname:integer;var optval; optlen:integer):integer; stdcall; far; external winsock;
Function sendto(socket:integer; data:pointer; datalen,flags:integer; var SockAddr:TSockAddr; AddrLen:Integer):integer; stdcall; far; external winsock;
Function recvfrom(socket:integer; data:pointer; datalen,flags:integer; var SockAddr:TSockAddr; var AddrLen:Integer):integer; stdcall; far; external winsock;
 
 
Function IpToLong(ip:string):LongInt;
 var
  x,i:byte;
  ipx:array[0..3] of byte;
  v:integer;
 begin
  Result:=0;
  longint(ipx):=0; i:=0;
  for x:=1 to length(ip) do
   if ip[x]='.' then begin
    inc(i);
    if i=4 then exit;
   end else begin
    if not (ip[x] in ['0'..'9']) then exit;
    v:=ipx[i]*10+ord(ip[x])-ord('0');
    if v>255 then exit;
    ipx[i]:=v;
   end;
  result:=longint(ipx);
 end;
 
Function HostToLong(AHost:string):LongInt;
 Var
  Host:PHost;
 begin
  Result:=IpToLong(AHost);
  if Result=0 then begin
   Host:=GetHostByName(PChar(AHost));
   if Host<>nil then Result:=longint(Host^.Addr^^);
  end; 
 end;
 
Function LongToIp(Long:LongInt):string;
 var
  ipx:array[0..3] of byte;
  i:byte;
 begin
  longint(ipx):=long;
  Result:='';
  for i:=0 to 3 do result:=result+IntToStr(ipx[i])+'.';
  SetLength(Result,Length(Result)-1);
 end;
 
//--- Server Side ------------------------------------------------------------------------
function StartServer(Port:word):integer;
 Var
  SockAddr:TSockAddr;
 begin
  Result:=socket(Internet,Stream,0);
  if Result=-1 then exit;
  FillChar(SockAddr,SizeOf(SockAddr),0);
  SockAddr.Family:=Internet;
  SockAddr.Port:=swap(Port);
  if (Bind(Result,SockAddr,SizeOf(SockAddr))<>0)
  or (Listen(Result,0)<>0) then begin
   CloseSocket(Result);
   Result:=-1;
  end;
 end;
 
function WaitClient(Server:integer):integer;
 var
  Client:TSockAddr;
  Size:integer;
 begin
  Size:=SizeOf(Client);
  Result:=Accept(Server,Client,Size);
 end;
 
function WaitClientEx(Server:integer; var ip:string):integer;
 var
  Client:TSockAddr;
  Size:integer;
 begin
  Size:=SizeOf(Client);
  Result:=Accept(Server,Client,Size);
  ip:=LongToIp(Client.Addr);
 end;
 
function SockReady(Socket:integer):boolean;
 var
  sockset:packed record
   count:integer;
   socks:{array[0..63] of} integer;
  end;
  timeval:TTimeOut;
 begin
  sockSet.count:=1;
  sockSet.socks:=Socket;
  timeval.sec  :=0;
  timeval.usec :=0;
  result:=Select(0,@sockSet,nil,nil,timeval)>0;
 end;
 
function SockAvail(Socket:integer):integer;
 var
  rdy:boolean;
 begin
  rdy:=SockReady(Socket); // before IoCtlSocket to be sure (?) that we don't get some data between the 2 calls
  if IoctlSocket(Socket, fIoNbRead,Result)<0 then
   Result:=-1
  else begin
   if (Result=0) and RDY then result:=-1; // SockReady is TRUE when Data ara Avaible AND when Socket is closed
  end;
 end;
 
function DataAvail(Var F:TextFile):integer;
 var
  s:integer;
 begin
 // cause of TexTFile Buffer, we need to check both Buffer & Socket !
  With TTextRec(F) do begin
   Result:=BufEnd-BufPos;
   s:=SockAvail(Handle);
  end;
  if Result=0 then Result:=s else if s>0 then Inc(Result,s);
 end;
 
Function BlockReadSock(Var F:TextFile; var s:string):boolean;
 Var
  Handle:THandle;
  Size:integer;
 begin
  Result:=False;
  Handle:=TTextRec(F).Handle;
  Repeat
   if (IoctlSocket(Handle, fIoNbRead, Size)<0) then exit;
   if Size=0 then exit
  until (Size>0);
  SetLength(s,Size);
  Recv(Handle,pchar(s),Size,0);
  Result:=True;
 end;
 
// Client Side--------------------------------------------------------------------------
function CallServer(Server:string; Port:word):integer;
 var
  SockAddr:TSockAddr;
 begin
  Result:=socket(Internet,Stream,0);
  if Result=-1 then exit;
  FillChar(SockAddr,SizeOf(SockAddr),0);
  SockAddr.Family:=Internet;
  SockAddr.Port:=swap(Port);
  SockAddr.Addr:=HostToLong(Server);
  if Connect(Result,SockAddr,SizeOf(SockAddr))<>0 then begin
   Disconnect(Result);
   Result:=-1;
  end;
 end;
 
// BroadCasting-------------------------------------
function StartBroadCast(Port:word):integer;
 Var
  SockAddr:TSockAddr;
  bc:integer;
 begin
  Result:=socket(Internet,Datagrams,17); // 17 for UDP ... work also with 0 ?!
  if Result=-1 then exit;
  FillChar(SockAddr,SizeOf(SockAddr),0);
  SockAddr.Family:=Internet;
  SockAddr.Port:=swap(Port);
//  SockAddr.Addr:=0; ?
  bc:=SO_BROADCAST;
  if (Bind(Result,SockAddr,SizeOf(SockAddr))<>0)
  or (setsockopt(Result,SOL_SOCKET,SO_BROADCAST,bc,SizeOf(bc))<>0) then begin
   CloseSocket(Result);
   Result:=-1;
  end;
 end;
 
function SendBroadCast(Server:integer; Port:word; s:string):integer;
 Var
  SockAddr:TSockAddr;
 begin
  SockAddr.Family:=Internet;
  SockAddr.Port:=swap(Port);
  SockAddr.Addr:=-1;
  Result:=SendTo(Server,@s[1],length(s),0,SockAddr,SizeOf(SockAddr));
 end;
 
function SendBroadCastTo(Server:integer; Port:word; ip,s:string):integer;
 Var
  SockAddr:TSockAddr;
 begin
  SockAddr.Family:=Internet;
  SockAddr.Port:=swap(Port);
  SockAddr.Addr:=IpToLong(ip);
  Result:=SendTo(Server,@s[1],length(s),0,SockAddr,SizeOf(SockAddr));
 end;
 
function ReadBroadCast(Server:integer; Port:word):string;
 Var
  SockAddr:TSockAddr;
  SockLen:integer;
  len:integer;
 begin
  FillChar(SockAddr,SizeOf(SockAddr),0);
  SockAddr.Family:=Internet;
  SockAddr.Port:=swap(Port);
  SockLen:=SizeOf(SockAddr);
  setlength(result,1024);
  len:=recvfrom(Server,@result[1],1024,0,SockAddr,SockLen);
  if len>0 then SetLength(result,len) else result:='';
 end;
 
function ReadBroadCastEx(Server:integer; Port:word; var ip:string):string;
 Var
  SockAddr:TSockAddr;
  SockLen:integer;
  len:integer;
 begin
  FillChar(SockAddr,SizeOf(SockAddr),0);
  SockAddr.Family:=Internet;
  SockAddr.Port:=swap(Port);
  SockLen:=SizeOf(SockAddr);
  setlength(result,1024);
  len:=recvfrom(Server,@result[1],1024,0,SockAddr,SockLen);
  if len>0 then SetLength(result,len) else result:='';
  ip:=LongToIp(SockAddr.Addr);
 end;
 
//------------ CrtSock -----------------
Var
 InitOk:boolean;
 
function OutputSock(Var F:TTextRec):integer; far;
 begin
 {$ifdef debug}writeln('out ',F.BufPtr);{$endif}
  if F.BufPos<>0 then begin
   Send(F.Handle,F.BufPtr,F.BufPos,0);
   F.BufPos:=0;
  end;
  Result:=0;
 end;
 
function InputSock(var F: TTextRec): Integer; far;
 Var
  Size:integer;
 begin
  F.BufEnd:=0;
  F.BufPos:=0;
  Result:=0;
  Repeat
   if (IoctlSocket(F.Handle, fIoNbRead, Size)<0) then begin
    EofSock:=True;
    exit;
   end;
  until (Size>=0);
  //if Size>0 then
  F.BufEnd:=Recv(F.Handle,F.BufPtr,F.BufSize,0);
  EofSock:=(F.BufEnd=0);
 {$ifdef debug}writeln('in  ',F.BufPtr);{$endif}
 end;
 
procedure Disconnect(Socket:integer);
 var
  dummy:array[0..1024] of char;
 begin
  ShutDown(Socket,1);
  repeat until recv(Socket,dummy,1024,0)<=0;
  CloseSocket(Socket);
 end;
 
function CloseSock(var F:TTextRec):integer; far;
 begin
  Disconnect(F.Handle);
  F.Handle:=-1;
  Result:=0;
 end;
 
function OpenSock(var F: TTextRec): Integer; far;
begin
  F.BufPos:=0;
  F.BufEnd:=0;
  if F.Mode = fmInput then begin // ReadLn
    EofSock:=False;
    F.InOutFunc := @InputSock;
    F.FlushFunc := nil;
  end else begin                 // WriteLn
    F.Mode := fmOutput;
    F.InOutFunc := @OutputSock;
    F.FlushFunc := @OutputSock;
  end;
  F.CloseFunc := @CloseSock;
  Result:=0;
end;
 
Procedure AssignCrtSock(Socket:integer; Var Input,Output:TextFile);
 begin
  with TTextRec(Input) do begin
    Handle := Socket;
    Mode := fmClosed;
    BufSize := SizeOf(Buffer);
    BufPtr := @Buffer;
    OpenFunc := @OpenSock;
  end;
  with TTextRec(Output) do begin
    Handle := Socket;
    Mode := fmClosed;
    BufSize := SizeOf(Buffer);
    BufPtr := @Buffer;
    OpenFunc := @OpenSock;
  end;
  Reset(Input);
  Rewrite(Output);
 end;
 
//----- Initialization/Finalization--------------------------------------------------
 
Procedure InitCrtSock;
 var
  wsaData:TWSAData;
 begin
  InitOk:=wsaStartup($101,wsaData)=0;
{$ifdef debug}allocconsole{$endif}
 end;
 
Procedure DoneCrtSock;
 begin
  if not InitOk then exit;
  if wsaIsBlocking then wsaCancelBlockingCall;
  wsaCleanup;
 end;
 
Initialization InitCrtSock;
 
Finalization DoneCrtSock;
 
end.