openblt/Host/Source/MicroBoot/interfaces/net/WSockets.pas

1551 lines
42 KiB
ObjectPascal

unit WSockets;
{
WSockets Version 1.20 - A Simple VCL Encapsulation of the WinSocket API
VCL Classes in this Unit:
TTCPClient - A TCP Client (derived from TCustomWSocket)
TTCPServer - A TCP Server (derived from TCustomWSocket)
TUDPClient - A UDP Client (derived from TCustomWSocket)
TUDPServer - A UDP Server (derived from TCustomWSocket)
Other classes ni this Unit:
TCustomWSocket - A generic base class for other socket classes
TClientList - A list class used only by the TTCPServer class
Legal issues:
Copyright (C) 1997 by Robert T. Palmqvist <robert.palmqvist@skanska.se>
This software is provided 'as-is', without any express or implied
warranty. In no event will the author be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented, you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
Credits go to:
Gary T. Desrosiers. His unit "Sockets" inspired me to write my own.
Martin Hall, Mark Towfig, Geoff Arnold, David Treadwell, Henry Sanders
and InfoMagic, Inc. for their Windows Help File "WinSock.hlp".
All the guys at Borland who gave us a marvellous tool named "Delphi"!
Recommended information sources:
Specification:
Windows Sockets Version 1.1 Specification
Textbook:
Quinn and Shute. "Windows Sockets Network Programming"
1996 by Addison-Wesley Publishing Company, Inc. ISBN 0-201-63372-8
World Wide Web:
http://www.sockets.com
http://www.stardust.com
Network News:
alt.winsock.programming
Frequently Asked Questions:
"WinSock Application FAQ" Mailto: info@lcs.com Subject: faq
Requests for Comments:
RFC 768 "User Datagram Protocol"
RFC 791 "Internet Protocol"
RFC 793 "Transmission Control Protocol"
}
interface
uses
Windows, WinSock, SysUtils, Classes, Messages, Forms;
const
WM_ASYNCSELECT = WM_USER + 1;
READ_BUFFER_SIZE = 1024;
MAX_LOOP = 100;
type
TSocketState = (ssNotStarted, ssClosed, ssConnected, ssListening, ssOpen);
TOnError = procedure(Sender: TObject; Error: integer; Msg: string) of object;
TOnData = procedure(Sender: TObject; Socket: TSocket) of object;
TOnAccept = procedure(Sender: TObject; Socket: TSocket) of object;
TOnConnect = procedure(Sender: TObject; Socket: TSocket) of object;
TOnClose = procedure(Sender: TObject; Socket: TSocket) of object;
TReadBuffer = array[1..READ_BUFFER_SIZE] of byte;
TClientList = class(TObject)
private
FSockets: TList;
protected
function GetSockets(Index: integer): TSocket;
function GetCount: integer;
public
constructor Create;
destructor Destroy; override;
function Add(Socket: TSocket): boolean;
procedure Delete(Socket: TSocket);
procedure Clear;
function IndexOf(Socket: TSocket): integer;
property Sockets[Index: integer]: TSocket read GetSockets; default;
property Count: integer read GetCount;
end;
TCustomWSocket = class(TComponent)
private
{WinSocket Information Private Fields}
FVersion: string;
FDescription: string;
FSystemStatus: string;
FMaxSockets: integer;
FMaxUDPSize: integer;
{End WinSocket Information Private Fields}
FProtocol: integer;
FType: integer;
FReadBuffer: TReadBuffer;
FLocalSocket: TSocket;
FSocketState: TSocketState;
FLastError: integer;
FOnError: TOnError;
protected
procedure SocketError(Error: integer);
function LastErrorDesc: string;
function GetLocalHostAddress: string;
function GetLocalHostName: string;
{Socket Helper Functions}
procedure SocketClose(var Socket: TSocket; Handle: HWND);
function SocketQueueSize(Socket: TSocket): longint;
procedure SocketWrite(Socket: TSocket; Flag: integer; Data: string);
function SocketRead(Socket: TSocket; Flag: integer): string;
function SocketWriteBuffer(Socket: TSocket; Buffer: Pointer; Size, Flag: integer): integer;
function SocketReadBuffer(Socket: TSocket; Buffer: Pointer; Size, Flag: integer): integer;
procedure SocketWriteTo(Socket: TSocket; Flag: integer; Data: string; var SockAddrIn: TSockAddrIn);
function SocketReadFrom(Socket: TSocket; Flag: integer; var SockAddrIn: TSockAddrIn): string;
function SocketWriteBufferTo(Socket: TSocket; Buffer: Pointer; Size, Flag: integer; var SockAddrIn: TSockAddrIn): integer;
function SocketReadBufferFrom(Socket: TSocket; Buffer: Pointer; Size, Flag: integer; var SockAddrIn: TSockAddrIn): integer;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
{Address and Port Resolving Helper Functions}
function GetSockAddrIn(Host, Port: string; var SockAddrIn: TSockAddrIn): boolean;
function GetAnySockAddrIn(Port: string; var SockAddrIn: TSockAddrIn): boolean;
function GetBroadcastSockAddrIn(Port: string; var SockAddrIn: TSockAddrIn): boolean;
function SockAddrInToName(SockAddrIn: TSockAddrIn): string;
function SockAddrInToAddress(SockAddrIn: TSockAddrIn): string;
function SockAddrInToPort(SockAddrIn: TSockAddrIn): string;
function SocketToName(Socket: TSocket): string;
function SocketToAddress(Socket: TSocket): string;
function SocketToPort(Socket: TSocket): string;
function PeerToName(Socket: TSocket): string;
function PeerToAddress(Socket: TSocket): string;
function PeerToPort(Socket: TSocket): string;
{WinSocket Information Properties}
property Version: string read FVersion;
property Description: string read FDescription;
property SystemStatus: string read FSystemStatus;
property MaxSockets: integer read FMaxSockets;
property MaxUDPSize: integer read FMaxUDPSize;
{End WinSocket Information Properties}
property LocalSocket: TSocket read FLocalSocket;
property SocketState: TSocketState read FSocketState;
property LastError: integer read FLastError;
property LocalHostAddress: string read GetLocalHostAddress;
property LocalHostName: string read GetLocalHostName;
published
property OnError: TOnError read FOnError write FOnError;
end;
TTCPClient = class(TCustomWSocket)
private
FHandle: HWND;
FHost: string;
FPort: string;
FOnData: TOnData;
FOnConnect: TOnConnect;
FOnClose: TOnClose;
protected
procedure WndProc(var AMsg: TMessage);
procedure OpenConnection(Socket: TSocket; Error: word);
procedure IncommingData(Socket: TSocket; Error: word);
procedure CloseConnection(Socket: TSocket; Error: word);
function GetPeerAddress: string;
function GetPeerPort: string;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Open;
procedure Close;
function Peek: string;
procedure Write(Data: string);
function Read: string;
function WriteBuffer(Buffer: Pointer; Size: integer): integer;
function ReadBuffer(Buffer: Pointer; Size: integer): integer;
property Handle: HWND read FHandle;
property PeerAddress: string read GetPeerAddress;
property PeerPort: string read GetPeerPort;
published
property Host: string read FHost write FHost;
property Port: string read FPort write FPort;
property OnData: TOnData read FOnData write FOnData;
property OnConnect: TOnConnect read FOnConnect write FOnConnect;
property OnClose: TOnClose read FOnClose write FOnClose;
end;
TTCPServer = class(TCustomWSocket)
private
FHandle: HWND;
FPort: string;
FOnData: TOnData;
FOnAccept: TOnAccept;
FOnClose: TOnClose;
FClients: TClientList;
protected
procedure WndProc(var AMsg: TMessage);
procedure OpenConnection(Socket: TSocket; Error: word);
procedure IncommingData(Socket: TSocket; Error: word);
procedure CloseConnection(Socket: TSocket; Error: word);
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Open;
procedure Close;
function Peek(Socket: TSocket): string;
procedure Write(Socket: TSocket; Data: string);
function Read(Socket: TSocket): string;
function WriteBuffer(Socket: TSocket; Buffer: Pointer; Size: integer): integer;
function ReadBuffer(Socket: TSocket; Buffer: Pointer; Size: integer): integer;
procedure Disconnect(Socket: TSocket);
property Handle: HWND read FHandle;
property Clients: TClientList read FClients;
published
property Port: string read FPort write FPort;
property OnData: TOnData read FOnData write FOnData;
property OnAccept: TOnAccept read FOnAccept write FOnAccept;
property OnClose: TOnClose read FOnClose write FOnClose;
end;
TUDPClient = class(TCustomWSocket)
private
FHandle: HWND;
FHost: string;
FPort: string;
FOnData: TOnData;
protected
procedure WndProc(var AMsg: TMessage);
procedure IncommingData(Socket: TSocket; Error: word);
function GetPeerAddress: string;
function GetPeerPort: string;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Open;
procedure Close;
function Peek: string;
procedure Write(Data: string);
function Read: string;
function WriteBuffer(Buffer: Pointer; Size: integer): integer;
function ReadBuffer(Buffer: Pointer; Size: integer): integer;
property Handle: HWND read FHandle;
property PeerAddress: string read GetPeerAddress;
property PeerPort: string read GetPeerPort;
published
property Host: string read FHost write FHost;
property Port: string read FPort write FPort;
property OnData: TOnData read FOnData write FOnData;
end;
TUDPServer = class(TCustomWSocket)
private
FHandle: HWND;
FPort: string;
FOnData: TOnData;
protected
procedure WndProc(var AMsg: TMessage);
procedure IncommingData(Socket: TSocket; Error: word);
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Open;
procedure Close;
function Peek(Socket: TSocket; var SockAddrIn: TSockAddrIn): string;
procedure Write(Socket: TSocket; Data: string; var SockAddrIn: TSockAddrIn);
function Read(Socket: TSocket; var SockAddrIn: TSockAddrIn): string;
function WriteBuffer(Socket: TSocket; Buffer: Pointer; Size: integer; var SockAddrIn: TSockAddrIn): integer;
function ReadBuffer(Socket: TSocket; Buffer: Pointer; Size: integer; var SockAddrIn: TSockAddrIn): integer;
property Handle: HWND read FHandle;
published
property Port: string read FPort write FPort;
property OnData: TOnData read FOnData write FOnData;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Samples', [TTCPClient, TTCPServer, TUDPClient, TUDPServer]);
end;
(**** TClientList Class ****)
constructor TClientList.Create;
begin
inherited Create;
FSockets:= TList.Create;
end;
destructor TClientList.Destroy;
begin
Clear;
FSockets.Free;
inherited Destroy;
end;
function TClientList.GetSockets(Index: integer): TSocket;
begin
Result:= TSocket(FSockets[Index]);
end;
function TClientList.GetCount: integer;
begin
Result:= FSockets.Count;
end;
function TClientList.Add(Socket: TSocket): boolean;
begin
Result:= (FSockets.Add(Ptr(Socket)) >= 0);
end;
procedure TClientList.Delete(Socket: TSocket);
var
i: integer;
begin
for i:= 0 to FSockets.Count-1 do
begin
if TSocket(FSockets[i]) = Socket then
begin
FSockets.Delete(i);
Break;
end;
end;
end;
procedure TClientList.Clear;
begin
FSockets.Clear;
end;
function TClientList.IndexOf(Socket: TSocket): integer;
var
i: integer;
begin
Result:= -1;
for i:= 0 to FSockets.Count-1 do
begin
if TSocket(FSockets[i]) = Socket then
begin
Result:= i;
Break;
end;
end;
end;
(**** TCustomWSocket Class ****)
constructor TCustomWSocket.Create(AOwner: TComponent);
var
WSAData: TWSAData;
begin
inherited Create(AOwner);
FProtocol:= IPPROTO_IP;
FType:= SOCK_RAW;
FLocalSocket:= INVALID_SOCKET;
FSocketState:= ssNotStarted;
FLastError:= WSAStartup($101, WSAData);
if FLastError = 0 then
begin
FSocketState:= ssClosed;
with WSAData do
begin
FVersion:= Concat(IntToStr(Hi(wVersion)),'.',(IntToStr(Lo(wVersion))));
FDescription:= String(szDescription);
FSystemStatus:= String(szSystemStatus);
FMaxSockets:= iMaxSockets;
FMaxUDPSize:= iMaxUDPDg;
end;
end
else
SocketError(FLastError);
end;
destructor TCustomWSocket.Destroy;
begin
if FLocalSocket <> INVALID_SOCKET then
closesocket(FLocalSocket);
if FSocketState <> ssNotStarted then
if WSACleanUp = SOCKET_ERROR then
SocketError(WSAGetLastError);
inherited Destroy;
end;
function TCustomWSocket.GetSockAddrIn(
Host, Port: string; var SockAddrIn: TSockAddrIn): boolean;
var
ProtoEnt: PProtoEnt;
ServEnt: PServEnt;
HostEnt: PHostEnt;
begin
Result:= false;
SockAddrIn.sin_family:= AF_INET;
ProtoEnt:= getprotobynumber(FProtocol);
if ProtoEnt = nil then
begin
SocketError(WSAGetLastError);
Exit;
end;
ServEnt:= getservbyname(PAnsiChar(AnsiString(Port)), ProtoEnt^.p_name);
if ServEnt = nil then
SockAddrIn.sin_port:= htons(StrToInt(Port))
else
SockAddrIn.sin_port:= ServEnt^.s_port;
SockAddrIn.sin_addr.s_addr:= inet_addr(PAnsiChar(AnsiString(Host)));
if SockAddrIn.sin_addr.s_addr = Integer(INADDR_NONE) then
begin
HostEnt:= gethostbyname(PAnsiChar(AnsiString(Host)));
if HostEnt = nil then
begin
SocketError(WSAGetLastError);
Exit;
end;
SockAddrIn.sin_addr.S_addr:= longint(plongint(HostEnt^.h_addr_list^)^);
end;
Result:= true;
end;
function TCustomWSocket.GetAnySockAddrIn(
Port: string; var SockAddrIn: TSockAddrIn): boolean;
var
ProtoEnt: PProtoEnt;
ServEnt: PServEnt;
begin
Result:= false;
SockAddrIn.sin_family:= AF_INET;
ProtoEnt:= getprotobynumber(FProtocol);
if ProtoEnt = nil then
Exit;
ServEnt:= getservbyname(PAnsiChar(AnsiString(Port)), ProtoEnt^.p_name);
if ServEnt = nil then
SockAddrIn.sin_port:= htons(StrToInt(Port))
else
SockAddrIn.sin_port:= ServEnt^.s_port;
SockAddrIn.sin_addr.s_addr:= INADDR_ANY;
Result:= true;
end;
function TCustomWSocket.GetBroadcastSockAddrIn(
Port: string; var SockAddrIn: TSockAddrIn): boolean;
var
ProtoEnt: PProtoEnt;
ServEnt: PServEnt;
begin
Result:= false;
SockAddrIn.sin_family:= AF_INET;
ProtoEnt:= getprotobynumber(FProtocol);
if ProtoEnt = nil then
Exit;
ServEnt:= getservbyname(PAnsiChar(AnsiString(Port)), ProtoEnt^.p_name);
if ServEnt = nil then
SockAddrIn.sin_port:= htons(StrToInt(Port))
else
SockAddrIn.sin_port:= ServEnt^.s_port;
SockAddrIn.sin_addr.s_addr:= Integer(INADDR_BROADCAST);
Result:= true;
end;
function TCustomWSocket.SockAddrInToName(SockAddrIn: TSockAddrIn): string;
var
HostEnt: PHostEnt;
begin
HostEnt:= gethostbyaddr(@SockAddrIn.sin_addr.S_addr, 4, AF_INET);
if HostEnt <> nil then
Result:= String(AnsiString(HostEnt.h_name));
end;
function TCustomWSocket.SockAddrInToAddress(SockAddrIn: TSockAddrIn): string;
begin
Result:= String(AnsiString(inet_ntoa(SockAddrIn.sin_addr)));
end;
function TCustomWSocket.SockAddrInToPort(SockAddrIn: TSockAddrIn): string;
begin
Result:= IntToStr(ntohs(SockAddrIn.sin_port));
end;
function TCustomWSocket.SocketToName(Socket: TSocket): string;
var
SockAddrIn: TSockAddrIn;
Len: integer;
HostEnt: PHostEnt;
begin
if Socket <> INVALID_SOCKET then
begin
Len:= SizeOf(SockAddrIn);
if getsockname(Socket, SockAddrIn, Len) <> SOCKET_ERROR then
begin
HostEnt:= gethostbyaddr(@SockAddrIn.sin_addr.S_addr, 4, AF_INET);
if HostEnt <> nil then
Result:= String(AnsiString(HostEnt.h_name));
end;
end;
end;
function TCustomWSocket.SocketToAddress(Socket: TSocket): string;
var
SockAddrIn: TSockAddrIn;
Len: integer;
begin
if Socket <> INVALID_SOCKET then
begin
Len:= SizeOf(SockAddrIn);
if getsockname(Socket, SockAddrIn, Len) <> SOCKET_ERROR then
Result:= String(AnsiString(inet_ntoa(SockAddrIn.sin_addr)));
end;
end;
function TCustomWSocket.SocketToPort(Socket: TSocket): string;
var
SockAddrIn: TSockAddrIn;
Len: integer;
begin
if Socket <> INVALID_SOCKET then
begin
Len:= SizeOf(SockAddrIn);
if getsockname(Socket, SockAddrIn, Len) <> SOCKET_ERROR then
Result:= IntToStr(ntohs(SockAddrIn.sin_port));
end;
end;
function TCustomWSocket.PeerToName(Socket: TSocket): string;
var
SockAddrIn: TSockAddrIn;
Len: integer;
HostEnt: PHostEnt;
begin
if Socket <> INVALID_SOCKET then
begin
Len:= SizeOf(SockAddrIn);
if getpeername(Socket, SockAddrIn, Len) <> SOCKET_ERROR then
begin
HostEnt:= gethostbyaddr(@SockAddrIn.sin_addr.S_addr, 4, AF_INET);
if HostEnt <> nil then
Result:= String(AnsiString(HostEnt.h_name));
end;
end;
end;
function TCustomWSocket.PeerToAddress(Socket: TSocket): string;
var
SockAddrIn: TSockAddrIn;
Len: integer;
begin
if Socket <> INVALID_SOCKET then
begin
Len:= SizeOf(SockAddrIn);
if getpeername(Socket, SockAddrIn, Len) <> SOCKET_ERROR then
Result:= String(AnsiString(inet_ntoa(SockAddrIn.sin_addr)));
end;
end;
function TCustomWSocket.PeerToPort(Socket: TSocket): string;
var
SockAddrIn: TSockAddrIn;
Len: integer;
begin
if Socket <> INVALID_SOCKET then
begin
Len:= SizeOf(SockAddrIn);
if getpeername(Socket, SockAddrIn, Len) <> SOCKET_ERROR then
Result:= IntToStr(ntohs(SockAddrIn.sin_port));
end;
end;
procedure TCustomWSocket.SocketClose(var Socket: TSocket; Handle: HWND);
var
RC: integer;
begin
if Socket <> INVALID_SOCKET then
begin
if WSAASyncSelect(Socket, Handle, WM_ASYNCSELECT, 0) <> 0 then
begin
SocketError(WSAGetLastError);
Exit;
end;
if shutdown(Socket, 1) <> 0 then
if WSAGetLastError <> WSAENOTCONN then
begin
SocketError(WSAGetLastError);
Exit;
end;
repeat
RC:= recv(Socket, FReadBuffer, SizeOf(TReadBuffer), 0);
until (RC = 0) or (RC = SOCKET_ERROR);
if closesocket(Socket) <> 0 then
SocketError(WSAGetLastError)
else
Socket:= INVALID_SOCKET;
end;
end;
function TCustomWSocket.SocketQueueSize(Socket: TSocket): longint;
var
Size: longint;
begin
Result:= 0;
if ioctlsocket(Socket, FIONREAD, Size) <> 0 then
SocketError(WSAGetLastError)
else
Result:= Size;
end;
procedure TCustomWSocket.SocketWrite(Socket: TSocket; Flag: integer; Data: string);
var
TotSent, ToSend, Sent, ErrorLoop: integer;
begin
if Data <> '' then
begin
ErrorLoop:= 0;
TotSent:= 0;
ToSend:= Length(Data);
repeat
Sent:= send(Socket, Data[TotSent+1], (ToSend-TotSent), Flag);
if Sent = SOCKET_ERROR then
begin
Inc(ErrorLoop);
if WSAGetLastError <> WSAEWOULDBLOCK then
begin
SocketError(WSAGetLastError);
Exit;
end;
end
else
Inc(TotSent, Sent);
until (TotSent >= ToSend) or (ErrorLoop > MAX_LOOP);
end;
end;
function TCustomWSocket.SocketRead(Socket: TSocket; Flag: integer): string;
var
Received: longint;
begin
Result:= '';
Received:= recv(Socket, FReadBuffer, SizeOf(TReadBuffer), Flag);
if Received = SOCKET_ERROR then
begin
if WSAGetLastError <> WSAEWOULDBLOCK then
SocketError(WSAGetLastError);
end
else
begin
SetLength(Result, Received);
Move(FReadBuffer, Result[1], Received);
end;
end;
function TCustomWSocket.SocketWriteBuffer(Socket: TSocket; Buffer: Pointer; Size, Flag: integer): integer;
begin
Result:= send(Socket, Buffer^, Size, Flag);
if Result = SOCKET_ERROR then
begin
Result:= 0;
if WSAGetLastError <> WSAEWOULDBLOCK then
SocketError(WSAGetLastError);
end;
end;
function TCustomWSocket.SocketReadBuffer(Socket: TSocket; Buffer: Pointer; Size, Flag: integer): integer;
begin
Result:= recv(Socket, Buffer^, Size, Flag);
if Result = SOCKET_ERROR then
begin
Result:= 0;
if WSAGetLastError <> WSAEWOULDBLOCK then
SocketError(WSAGetLastError);
end;
end;
procedure TCustomWSocket.SocketWriteTo(Socket: TSocket; Flag: integer; Data: string; var SockAddrIn: TSockAddrIn);
var
TotSent, ToSend, Sent, ErrorLoop: integer;
begin
if Data <> '' then
begin
ErrorLoop:= 0;
TotSent:= 0;
ToSend:= Length(Data);
repeat
Sent:= sendto(Socket, Data[TotSent+1], (ToSend-TotSent), Flag, SockAddrIn, SizeOf(SockAddrIn));
if Sent = SOCKET_ERROR then
begin
Inc(ErrorLoop);
if WSAGetLastError <> WSAEWOULDBLOCK then
begin
SocketError(WSAGetLastError);
Exit;
end;
end
else
Inc(TotSent, Sent);
until (TotSent >= ToSend) or (ErrorLoop > MAX_LOOP);
end;
end;
function TCustomWSocket.SocketReadFrom(Socket: TSocket; Flag: integer; var SockAddrIn: TSockAddrIn): string;
var
Len: integer;
Received: longint;
begin
Len:= SizeOf(SockAddrIn);
Received:= recvfrom(Socket, FReadBuffer, SizeOf(TReadBuffer), Flag, SockAddrIn, Len);
if Received = SOCKET_ERROR then
begin
if WSAGetLastError <> WSAEWOULDBLOCK then
SocketError(WSAGetLastError);
end
else
begin
SetLength(Result, Received);
Move(FReadBuffer, Result[1], Received);
end;
end;
function TCustomWSocket.SocketWriteBufferTo(Socket: TSocket; Buffer: Pointer; Size, Flag: integer; var SockAddrIn: TSockAddrIn): integer;
begin
Result:= sendto(Socket, Buffer^, Size, Flag, SockAddrIn, SizeOf(SockAddrIn));
if Result = SOCKET_ERROR then
begin
Result:= 0;
if WSAGetLastError <> WSAEWOULDBLOCK then
SocketError(WSAGetLastError);
end;
end;
function TCustomWSocket.SocketReadBufferFrom(Socket: TSocket; Buffer: Pointer; Size, Flag: integer; var SockAddrIn: TSockAddrIn): integer;
var
Len: integer;
begin
Len:= SizeOf(SockAddrIn);
Result:= recvfrom(Socket, Buffer^, Size, Flag, SockAddrIn, Len);
if Result = SOCKET_ERROR then
begin
Result:= 0;
if WSAGetLastError <> WSAEWOULDBLOCK then
SocketError(WSAGetLastError);
end;
end;
procedure TCustomWSocket.SocketError(Error: integer);
begin
FLastError:= Error;
if Assigned(FOnError) then
FOnError(Self, FLastError, LastErrorDesc);
end;
function TCustomWSocket.LastErrorDesc: string;
begin
case FLastError of
WSAEINTR : Result:= 'Interrupted system call';
WSAEBADF : Result:= 'Bad file number';
WSAEACCES : Result:= 'Permission denied';
WSAEFAULT : Result:= 'Bad address';
WSAEINVAL : Result:= 'Invalid argument';
WSAEMFILE : Result:= 'Too many open files';
WSAEWOULDBLOCK : Result:= 'Operation would block';
WSAEINPROGRESS : Result:= 'Operation now in progress';
WSAEALREADY : Result:= 'Operation already in progress';
WSAENOTSOCK : Result:= 'Socket operation on nonsocket';
WSAEDESTADDRREQ : Result:= 'Destination address required';
WSAEMSGSIZE : Result:= 'Message too long';
WSAEPROTOTYPE : Result:= 'Protocol wrong type for socket';
WSAENOPROTOOPT : Result:= 'Protocol not available';
WSAEPROTONOSUPPORT : Result:= 'Protocol not supported';
WSAESOCKTNOSUPPORT : Result:= 'Socket not supported';
WSAEOPNOTSUPP : Result:= 'Operation not supported on socket';
WSAEPFNOSUPPORT : Result:= 'Protocol family not supported';
WSAEAFNOSUPPORT : Result:= 'Address family not supported';
WSAEADDRINUSE : Result:= 'Address already in use';
WSAEADDRNOTAVAIL : Result:= 'Can''t assign requested address';
WSAENETDOWN : Result:= 'Network is down';
WSAENETUNREACH : Result:= 'Network is unreachable';
WSAENETRESET : Result:= 'Network dropped connection on reset';
WSAECONNABORTED : Result:= 'Software caused connection abort';
WSAECONNRESET : Result:= 'Connection reset by peer';
WSAENOBUFS : Result:= 'No buffer space available';
WSAEISCONN : Result:= 'Socket is already connected';
WSAENOTCONN : Result:= 'Socket is not connected';
WSAESHUTDOWN : Result:= 'Can''t send after socket shutdown';
WSAETOOMANYREFS : Result:= 'Too many references:can''t splice';
WSAETIMEDOUT : Result:= 'Connection timed out';
WSAECONNREFUSED : Result:= 'Connection refused';
WSAELOOP : Result:= 'Too many levels of symbolic links';
WSAENAMETOOLONG : Result:= 'File name is too long';
WSAEHOSTDOWN : Result:= 'Host is down';
WSAEHOSTUNREACH : Result:= 'No route to host';
WSAENOTEMPTY : Result:= 'Directory is not empty';
WSAEPROCLIM : Result:= 'Too many processes';
WSAEUSERS : Result:= 'Too many users';
WSAEDQUOT : Result:= 'Disk quota exceeded';
WSAESTALE : Result:= 'Stale NFS file handle';
WSAEREMOTE : Result:= 'Too many levels of remote in path';
WSASYSNOTREADY : Result:= 'Network subsystem is unusable';
WSAVERNOTSUPPORTED : Result:= 'Winsock DLL cannot support this application';
WSANOTINITIALISED : Result:= 'Winsock not initialized';
WSAHOST_NOT_FOUND : Result:= 'Host not found';
WSATRY_AGAIN : Result:= 'Non authoritative - host not found';
WSANO_RECOVERY : Result:= 'Non recoverable error';
WSANO_DATA : Result:= 'Valid name, no data record of requested type'
else
Result:= 'Not a Winsock error';
end;
end;
function TCustomWSocket.GetLocalHostAddress: string;
var
SockAddrIn: TSockAddrIn;
HostEnt: PHostEnt;
szHostName: array[0..128] of ansichar;
begin
if gethostname(szHostName, 128) = 0 then
begin
HostEnt:= gethostbyname(szHostName);
if HostEnt = nil then
Result:= ''
else
begin
SockAddrIn.sin_addr.S_addr:= longint(plongint(HostEnt^.h_addr_list^)^);
Result:= String(AnsiString(inet_ntoa(SockAddrIn.sin_addr)));
end;
end
else
SocketError(WSAGetLastError);
end;
function TCustomWSocket.GetLocalHostName: string;
var
szHostName: array[0..128] of ansichar;
begin
if gethostname(szHostName, 128) = 0 then
Result:= String(AnsiString(szHostName))
else
SocketError(WSAGetLastError);
end;
(**** TTCPClient Class ****)
constructor TTCPClient.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FHandle:= AllocateHWnd(WndProc);
FProtocol:= IPPROTO_TCP;
FType:= SOCK_STREAM;
end;
destructor TTCPClient.Destroy;
begin
Close;
DeallocateHWnd(FHandle);
inherited Destroy;
end;
procedure TTCPClient.OpenConnection(Socket: TSocket; Error: word);
var
EventMask: longint;
begin
if Error <> 0 then
SocketError(Error)
else
begin
EventMask:= FD_READ or FD_CLOSE;
if WSAASyncSelect(FLocalSocket, FHandle, WM_ASYNCSELECT, EventMask) <> 0 then
SocketError(WSAGetLastError)
else
begin
if Assigned(FOnConnect) then
FOnConnect(Self, Socket);
FSocketState:= ssConnected;
end;
end;
end;
procedure TTCPClient.CloseConnection(Socket: TSocket; Error: word);
begin
if Error = WSAENETDOWN then
SocketError(Error)
else
begin
if Assigned(FOnClose) then
FOnClose(Self, Socket);
Close;
end;
end;
procedure TTCPClient.IncommingData(Socket: TSocket; Error: word);
begin
if Error <> 0 then
SocketError(Error)
else
if Assigned(FOnData) then
FOnData(Self, Socket);
end;
procedure TTCPClient.WndProc(var AMsg: TMessage);
var
Error: word;
begin
with AMsg do
case Msg of
WM_ASYNCSELECT:
begin
if (FSocketState = ssClosed) then
Exit;
Error:= WSAGetSelectError(LParam);
case WSAGetSelectEvent(LParam) of
FD_READ : IncommingData(WParam, Error);
FD_CONNECT: OpenConnection(WParam, Error);
FD_CLOSE : CloseConnection(WParam, Error);
else
if Error <> 0 then
SocketError(Error);
end;
end;
else
Result:= DefWindowProc(FHandle, Msg, WParam, LParam);
end;
end;
procedure TTCPClient.Open;
var
SockAddrIn: TSockAddrIn;
SockOpt: LongBool;
EventMask: longint;
begin
if (FSocketState <> ssClosed) then
Exit;
if not GetSockAddrIn(FHost, FPort, SockAddrIn) then
Exit;
FLocalSocket:= socket(PF_INET, FType, 0);
if FLocalSocket = INVALID_SOCKET then
begin
SocketError(WSAGetLastError);
Exit;
end;
EventMask:= (FD_CONNECT or FD_READ or FD_CLOSE);
if WSAASyncSelect(FLocalSocket, FHandle, WM_ASYNCSELECT, EventMask) <> 0 then
begin
SocketError(WSAGetLastError);
closesocket(FLocalSocket);
Exit;
end;
SockOpt:= true; {Enable OOB Data inline}
if setsockopt(FLocalSocket, SOL_SOCKET, SO_OOBINLINE, PAnsiChar(@SockOpt), SizeOf(SockOpt)) <> 0 then
begin
SocketError(WSAGetLastError);
closesocket(FLocalSocket);
Exit;
end;
if connect(FLocalSocket, SockAddrIn, SizeOf(SockAddrIn)) <> 0 then
begin
if WSAGetLastError <> WSAEWOULDBLOCK then
begin
SocketError(WSAGetLastError);
closesocket(FLocalSocket);
Exit;
end;
end;
FSocketState:= ssOpen;
end;
procedure TTCPClient.Close;
begin
if (FSocketState = ssNotStarted) or (FSocketState = ssClosed) then
Exit;
SocketClose(FLocalSocket, FHandle);
if FLocalSocket = INVALID_SOCKET then
FSocketState:= ssClosed;
end;
procedure TTCPClient.Write(Data: string);
begin
SocketWrite(FLocalSocket, 0, Data);
end;
function TTCPClient.Read: string;
begin
Result:= SocketRead(FLocalSocket, 0);
end;
function TTCPClient.Peek: string;
begin
Result:= SocketRead(FLocalSocket, MSG_PEEK);
end;
function TTCPClient.WriteBuffer(Buffer: Pointer; Size: integer): integer;
begin
Result:= SocketWriteBuffer(FLocalSocket, Buffer, Size, 0);
end;
function TTCPClient.ReadBuffer(Buffer: Pointer; Size: integer): integer;
begin
Result:= SocketReadBuffer(FLocalSocket, Buffer, Size, 0);
end;
function TTCPClient.GetPeerAddress: string;
begin
Result:= PeerToAddress(FLocalSocket);
end;
function TTCPClient.GetPeerPort: string;
begin
Result:= PeerToPort(FLocalSocket);
end;
(**** TTCPServer Class ****)
constructor TTCPServer.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FHandle:= AllocateHWnd(WndProc);
FProtocol:= IPPROTO_TCP;
FType:= SOCK_STREAM;
FClients:= TClientList.Create;
end;
destructor TTCPServer.Destroy;
begin
Close;
DeallocateHWnd(FHandle);
FClients.Free;
inherited Destroy;
end;
procedure TTCPServer.OpenConnection(Socket: TSocket; Error: word);
var
Len: integer;
NewSocket: TSocket;
SockAddrIn: TSockAddrIn;
SockOpt: LongBool;
EventMask: longint;
begin
if Error <> 0 then
SocketError(Error)
else
begin
Len:= SizeOf(SockAddrIn);
//{$IFDEF VER100} // Delphi 3
NewSocket:= accept(FLocalSocket, @SockAddrIn, @Len);
//{$ELSE} // Delphi 2
//NewSocket:= accept(FLocalSocket, SockAddrIn, Len);
//{$ENDIF}
if NewSocket = INVALID_SOCKET then
begin
SocketError(WSAGetLastError);
Exit;
end;
EventMask:= (FD_READ or FD_CLOSE);
if WSAASyncSelect(NewSocket, FHandle, WM_ASYNCSELECT, EventMask) <> 0 then
begin
SocketError(WSAGetLastError);
closesocket(NewSocket);
Exit;
end;
SockOpt:= true; {Enable OOB Data inline}
if setsockopt(NewSocket, SOL_SOCKET, SO_OOBINLINE , PAnsiChar(@SockOpt), SizeOf(SockOpt)) <> 0 then
begin
SocketError(WSAGetLastError);
closesocket(NewSocket);
Exit;
end;
if not FClients.Add(NewSocket) then
SocketClose(NewSocket, FHandle)
else
if Assigned(FOnAccept) then
FOnAccept(Self, NewSocket);
end;
end;
procedure TTCPServer.CloseConnection(Socket: TSocket; Error: word);
begin
if Error = WSAENETDOWN then
SocketError(Error)
else
begin
if Assigned(FOnClose) then
FOnClose(Self, Socket);
Disconnect(Socket);
end;
end;
procedure TTCPServer.IncommingData(Socket: TSocket; Error: word);
begin
if Error <> 0 then
SocketError(Error)
else
if Assigned(FOnData) then
FOnData(Self, Socket);
end;
procedure TTCPServer.WndProc(var AMsg: TMessage);
var
Error: word;
begin
with AMsg do
case Msg of
WM_ASYNCSELECT:
begin
if (FSocketState = ssClosed) then
Exit;
Error:= WSAGetSelectError(LParam);
case WSAGetSelectEvent(LParam) of
FD_READ : IncommingData(WParam, Error);
FD_ACCEPT: OpenConnection(WParam, Error);
FD_CLOSE : CloseConnection(WParam, Error);
else
if Error <> 0 then
SocketError(Error);
end;
end;
else
Result:= DefWindowProc(FHandle, Msg, WParam, LParam);
end;
end;
procedure TTCPServer.Open;
var
SockAddrIn: TSockAddrIn;
begin
if (FSocketState <> ssClosed) then
Exit;
if not GetAnySockAddrIn(FPort, SockAddrIn) then
Exit;
FLocalSocket:= socket(PF_INET, FType, 0);
if FLocalSocket = INVALID_SOCKET then
begin
SocketError(WSAGetLastError);
Exit;
end;
if WSAASyncSelect(FLocalSocket, FHandle, WM_ASYNCSELECT, FD_ACCEPT) <> 0 then
begin
SocketError(WSAGetLastError);
closesocket(FLocalSocket);
Exit;
end;
if bind(FLocalSocket, SockAddrIn, SizeOf(SockAddrIn)) <> 0 then
begin
SocketError(WSAGetLastError);
closesocket(FLocalSocket);
Exit;
end;
if listen(FLocalSocket, 5) <> 0 then
begin
SocketError(WSAGetLastError);
closesocket(FLocalSocket);
Exit;
end;
FSocketState:= ssListening;
end;
procedure TTCPServer.Close;
var
i: integer;
Dummy: TSocket;
begin
if (FSocketState = ssNotStarted) or (FSocketState = ssClosed) then
Exit;
for i:= 0 to FClients.Count-1 do
begin
Dummy:= FClients[i];
SocketClose(Dummy, FHandle);
end;
FClients.Clear;
SocketClose(FLocalSocket, FHandle);
if FLocalSocket = INVALID_SOCKET then
FSocketState:= ssClosed;
end;
procedure TTCPServer.Write(Socket: TSocket; Data: string);
begin
SocketWrite(Socket, 0, Data);
end;
function TTCPServer.Read(Socket: TSocket): string;
begin
Result:= SocketRead(Socket, 0);
end;
function TTCPServer.Peek(Socket: TSocket): string;
begin
Result:= SocketRead(Socket, MSG_PEEK);
end;
function TTCPServer.WriteBuffer(Socket: TSocket; Buffer: Pointer; Size: integer): integer;
begin
Result:= SocketWriteBuffer(Socket, Buffer, Size, 0);
end;
function TTCPServer.ReadBuffer(Socket: TSocket; Buffer: Pointer; Size: integer): integer;
begin
Result:= SocketReadBuffer(Socket, Buffer, Size, 0);
end;
procedure TTCPServer.Disconnect(Socket: TSocket);
begin
FClients.Delete(Socket);
SocketClose(Socket, FHandle);
end;
(**** TUDPClient Class ****)
constructor TUDPClient.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FHandle:= AllocateHWnd(WndProc);
FProtocol:= IPPROTO_UDP;
FType:= SOCK_DGRAM;
end;
destructor TUDPClient.Destroy;
begin
Close;
DeallocateHWnd(FHandle);
inherited Destroy;
end;
procedure TUDPClient.IncommingData(Socket: TSocket; Error: word);
begin
if Error <> 0 then
SocketError(Error)
else
if Assigned(FOnData) then
FOnData(Self, Socket);
end;
procedure TUDPClient.WndProc(var AMsg: TMessage);
var
Error: word;
begin
with AMsg do
case Msg of
WM_ASYNCSELECT:
begin
if (FSocketState = ssClosed) then
Exit;
Error:= WSAGetSelectError(LParam);
case WSAGetSelectEvent(LParam) of
FD_READ : IncommingData(WParam, Error);
else
if Error <> 0 then
SocketError(Error);
end;
end;
else
Result:= DefWindowProc(FHandle, Msg, WParam, LParam);
end;
end;
procedure TUDPClient.Open;
var
SockAddrIn: TSockAddrIn;
begin
if (FSocketState <> ssClosed) then
Exit;
if not GetSockAddrIn(FHost, FPort, SockAddrIn) then
Exit;
FLocalSocket:= socket(PF_INET, FType, 0);
if FLocalSocket = INVALID_SOCKET then
begin
SocketError(WSAGetLastError);
Exit;
end;
if WSAASyncSelect(FLocalSocket, FHandle, WM_ASYNCSELECT, FD_READ) <> 0 then
begin
SocketError(WSAGetLastError);
closesocket(FLocalSocket);
Exit;
end;
if connect(FLocalSocket, SockAddrIn, SizeOf(SockAddrIn)) <> 0 then
begin
if WSAGetLastError <> WSAEWOULDBLOCK then
begin
SocketError(WSAGetLastError);
closesocket(FLocalSocket);
Exit;
end;
end;
FSocketState:= ssOpen;
end;
procedure TUDPClient.Close;
begin
if (FSocketState = ssNotStarted) or (FSocketState = ssClosed) then
Exit;
SocketClose(FLocalSocket, FHandle);
if FLocalSocket = INVALID_SOCKET then
FSocketState:= ssClosed;
end;
procedure TUDPClient.Write(Data: string);
begin
SocketWrite(FLocalSocket, 0, Data);
end;
function TUDPClient.Read: string;
begin
Result:= SocketRead(FLocalSocket, 0);
end;
function TUDPClient.Peek: string;
begin
Result:= SocketRead(FLocalSocket, MSG_PEEK);
end;
function TUDPClient.WriteBuffer(Buffer: Pointer; Size: integer): integer;
begin
Result:= SocketWriteBuffer(FLocalSocket, Buffer, Size, 0);
end;
function TUDPClient.ReadBuffer(Buffer: Pointer; Size: integer): integer;
begin
Result:= SocketReadBuffer(FLocalSocket, Buffer, Size, 0);
end;
function TUDPClient.GetPeerAddress: string;
begin
Result:= PeerToAddress(FLocalSocket);
end;
function TUDPClient.GetPeerPort: string;
begin
Result:= PeerToPort(FLocalSocket);
end;
(**** TUDPServer Class ****)
constructor TUDPServer.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FHandle:= AllocateHWnd(WndProc);
FProtocol:= IPPROTO_UDP;
FType:= SOCK_DGRAM;
end;
destructor TUDPServer.Destroy;
begin
Close;
DeallocateHWnd(FHandle);
inherited Destroy;
end;
procedure TUDPServer.IncommingData(Socket: TSocket; Error: word);
begin
if Error <> 0 then
SocketError(Error)
else
if Assigned(FOnData) then
FOnData(Self, Socket);
end;
procedure TUDPServer.WndProc(var AMsg: TMessage);
var
Error: word;
begin
with AMsg do
case Msg of
WM_ASYNCSELECT:
begin
if (FSocketState = ssClosed) then
Exit;
Error:= WSAGetSelectError(LParam);
case WSAGetSelectEvent(LParam) of
FD_READ : IncommingData(WParam, Error);
else
if Error <> 0 then
SocketError(Error);
end;
end;
else
Result:= DefWindowProc(FHandle, Msg, WParam, LParam);
end;
end;
procedure TUDPServer.Open;
var
SockAddrIn: TSockAddrIn;
SockOpt: LongBool;
begin
if (FSocketState <> ssClosed) then
Exit;
if not GetAnySockAddrIn(FPort, SockAddrIn) then
Exit;
FLocalSocket:= socket(PF_INET, FType, 0);
if FLocalSocket = INVALID_SOCKET then
begin
SocketError(WSAGetLastError);
Exit;
end;
if WSAASyncSelect(FLocalSocket, FHandle, WM_ASYNCSELECT, FD_READ) <> 0 then
begin
SocketError(WSAGetLastError);
closesocket(FLocalSocket);
Exit;
end;
SockOpt:= true; {Enable Broadcasting on this Socket}
if setsockopt(FLocalSocket, SOL_SOCKET, SO_BROADCAST, PAnsiChar(@SockOpt), SizeOf(SockOpt)) <> 0 then
begin
SocketError(WSAGetLastError);
closesocket(FLocalSocket);
Exit;
end;
if bind(FLocalSocket, SockAddrIn, SizeOf(SockAddrIn)) <> 0 then
begin
SocketError(WSAGetLastError);
closesocket(FLocalSocket);
Exit;
end;
FSocketState:= ssListening;
end;
procedure TUDPServer.Close;
begin
if (FSocketState = ssNotStarted) or (FSocketState = ssClosed) then
Exit;
SocketClose(FLocalSocket, FHandle);
if FLocalSocket = INVALID_SOCKET then
FSocketState:= ssClosed;
end;
procedure TUDPServer.Write(Socket: TSocket; Data: string; var SockAddrIn: TSockAddrIn);
begin
SocketWriteTo(Socket, 0, Data, SockAddrIn);
end;
function TUDPServer.Read(Socket: TSocket; var SockAddrIn: TSockAddrIn): string;
begin
Result:= SocketReadFrom(Socket, 0, SockAddrIn);
end;
function TUDPServer.Peek(Socket: TSocket; var SockAddrIn: TSockAddrIn): string;
begin
Result:= SocketReadFrom(Socket, MSG_PEEK, SockAddrIn);
end;
function TUDPServer.WriteBuffer(Socket: TSocket; Buffer: Pointer; Size: integer; var SockAddrIn: TSockAddrIn): integer;
begin
Result:= SocketWriteBufferTo(Socket, Buffer, Size, 0, SockAddrIn);
end;
function TUDPServer.ReadBuffer(Socket: TSocket; Buffer: Pointer; Size: integer; var SockAddrIn: TSockAddrIn): integer;
begin
Result:= SocketReadBufferFrom(Socket, Buffer, Size, 0, SockAddrIn);
end;
end.