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.