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.