www.pudn.com > IPXControl.rar > IPXCtl.pas
//------------------------------------------------------------------//
// Copyright (c) 2002, 智能认知工作室.
// All rights reserved.
//
// 文件名称: MainUnit.pas
// 文件标识:
//
// 摘 要: IPX协议通讯控件(Delphi 6)
//
// 当前版本: 1.0
// 作 者: Rain (wangzp.try2it.com)
// 网 站: http://www.try2it.com
// 完成日期: 2002年08月20日
//
// 取代版本: 这是第一版
//------------------------------------------------------------------//
unit IPXCtl;
interface
uses Windows, Winsock2, Classes, Messages;
type
//出错处理
TSockErrEvent = procedure(Sender : TObject; ErrMsg : string) of Object;
TSocketEvent = procedure(Sender : TObject; Socket : TSocket) of Object;
TSocketArray = array of TSocket;
//IPX/SPX客户端类
TIPXClientSocket = class(TComponent)
private{对外不可见}
FActive : Boolean;
//保存与服务器相连的套接字
FSocket : TSocket;
//处理消息的窗口句柄
FWnd : HWND;
//节点地址
Fsa_nodenum : string;
//内部网络号
Fsa_netnum : string;
//内部端口
Fsa_socket : string;
//
FOnRead : TNotifyEvent;
FOnWrite : TNotifyEvent;
FOnConnected : TNotifyEvent;
FOnDisConnect : TNotifyEvent;
FOnError : TSockErrEvent;
protected{只对子孙可见}
procedure WndProc(var Msg : TMessage);
public{对外可见,出现在.后}
constructor Create(AOwner : TComponent); override;
destructor Destroy; override;
procedure Open;
procedure Close;
function SendBuf(var Buf; BufLen : Integer) : Integer;
function RecvBuf(var Buf; BufLen : Integer) : Integer;
property Handle : HWND read FWnd;
property Active : Boolean read FActive;
published{出现在Object Inspector中}
property sa_nodenum : string read Fsa_NodeNum write Fsa_NodeNum;
property sa_netnum : string read Fsa_NetNum write Fsa_NetNum;
property sa_socket : string read Fsa_Socket write Fsa_Socket;
property OnRead : TNotifyEvent read FOnRead write FOnRead;
property OnWrite : TNotifyEvent read FOnWrite write FOnWrite;
property OnConnected : TNotifyEvent read FOnConnected write FOnConnected;
property OnDisConnect : TNotifyEvent read FOnDisConnect write FOnDisConnect;
property OnError : TSockErrEvent read FOnError write FOnError;
end;
//IPXSPX服务器端类
TIPXServerSocket = class(TComponent)
private
FActive : Boolean;
//客户端连接过来的套接字
FClientSocket : TSocketArray;
FListenSocket : TSocket;
//当前活动的连接数
FActiveClientCount : Integer;
//消息窗口句柄
FWnd : HWND;
//服务器端绑定的端口
Fsa_socket : string;
//客户端发数据时发生
FOnRead : TSocketEvent;
//服务器发送数据时发生
FOnWrite : TSocketEvent;
FOnError : TSockErrEvent;
//服务器接受一个客户连接时发生
FOnClientConnected : TSocketEvent;
//一个客户端断开时发生
FOnClientDisconnect : TSocketEvent;
//当服务器建立侦听后发生
FOnListen : TSocketEvent;
function AcceptConnect : Boolean;
protected
procedure WndProc(var Msg : TMessage);
public
constructor Create(AOwner : TComponent); override;
destructor Destroy; override;
procedure Open;
procedure Close;
function SendBuf(ClientSocket : TSocket; var Buf; BufLen : Integer) : Integer;
function RecvBuf(ClientSocket : TSocket; var Buf; BufLen : Integer) : Integer;
property Handle : HWND read FWnd;
property ActiveClientCount : Integer read FActiveClientCount;
property Active : Boolean read FActive;
property Connections : TSocketArray read FClientSocket;
published
property sa_socket : string read Fsa_socket write Fsa_socket;
property OnListen : TSocketEvent read FOnListen write FOnListen;
property OnRead : TSocketEvent read FOnRead write FOnRead;
property OnWrite : TSocketEvent read FOnWrite write FOnWrite;
property OnClientConnected : TSocketEvent read FOnClientConnected write FOnClientConnected;
property OnClientDisconnect : TSocketEvent read FOnClientDisconnect write FOnClientDisconnect;
property OnError : TSockErrEvent read FOnError write FOnError;
end;
procedure Register;
//对在初始化中调用的WSAStartup进行判断是否成功
function CanUseWinSock : Boolean;
//字符串转换为IPX地址
procedure ATOH(Src : string; var Dest : array of Char; DestLen : Integer);overload;
procedure ATOH(Src : string; var Dest : u_short; DestLen : Integer);overload;
function BTOH(ch : char) : Char;
var
WSAData : TWSAData;
WSAStartError : Integer;
implementation
uses SysUtils, Forms;
const
WM_SOCKET = WM_USER + $0101; //异步消息
WM_SOCKET_SERVER = WM_USER + $0102;
procedure Register;
begin
RegisterComponents('Internet',[TIPXServerSocket, TIPXClientSocket]);
end;
//对在初始化中调用的WSAStartup进行判断是否成功
function CanUseWinSock : Boolean;
begin
Result := (WSAStartError = 0) and (WSAData.wVersion = WINSOCK_VERSION);
end;
//返回数字或字母的十六进制值
function BTOH(ch : char) : Char;
begin
if (ch >= '0') and (ch <= '9') then
begin
Result := Char(Ord(ch)- Ord('0'));
Exit;
end;
if (ch >= 'A') and (ch <= 'Z') then
begin
Result := Char((Ord(ch) - Ord('A')) + $0A);
Exit;
end;
if (ch >= 'a') and (ch <= 'z') then
begin
Result := Char((Ord(ch) - Ord('a')) + $0A);
Exit;
end;
Result := Char(255);
end;
procedure ATOH(Src : string; var Dest : array of char; DestLen : Integer);
var
srcptr : Char;
i, j : Integer;
begin
i := 0; j := 1;
while DestLen > 0 do
begin
srcptr := Src[j];
Dest[i] := char(Byte(BTOH(srcptr)) shl 4);
srcptr := Src[j + 1];
Dest[i] := char(Byte(Dest[i]) + Byte(BTOH(srcptr)));
i := i + 1;
j := j + 2;
DestLen := DestLen - 1;
end;
end;
procedure ATOH(Src : string; var Dest : u_short; DestLen : Integer);
var
srcptr : Char;
j : Integer;
Destptr : PByte;
begin
j := 1;
Destptr := PByte(@Dest);
while DestLen > 0 do
begin
srcptr := Src[j];
Destptr^ := Byte(BTOH(srcptr)) shl 4;
srcptr := Src[j + 1];
Destptr^ := Destptr^ + Byte(BTOH(srcptr));
Inc(DestPtr);
j := j + 2;
DestLen := DestLen - 1;
end;
end;
{TIPXClientSocket}
constructor TIPXClientSocket.Create(AOwner : TComponent);
begin
Inherited;
FSocket := INVALID_SOCKET;
FWnd := AllocateHwnd(WndProc);
FActive := False;
end;
destructor TIPXClientSocket.Destroy;
begin
Close;
DeAllocateHwnd(FWnd);
Inherited;
end;
procedure TIPXClientSocket.WndProc(var Msg : TMessage);
begin
//不是网络事件
if Msg.Msg <> WM_SOCKET then
begin
Msg.Result := DefWindowProc(FWnd, Msg.Msg, Msg.WParam, Msg.LParam);
Exit;
end;
if TSocket(Msg.WParam) <> FSocket then Exit;
if WSAGetSelectError(Msg.LParam) <> 0 then
begin
if Assigned(FOnError) then
FOnError(Self,'网络事件消息出错');
Exit;
end;
case WSAGetSelectEvent(Msg.LParam) of
FD_CONNECT :
begin
if Assigned(FOnConnected) then FOnConnected(Self);
end;
FD_READ :
begin
if Assigned(FOnRead) then FOnRead(self);
end;
FD_WRITE :
begin
if Assigned(FOnWrite) then FOnWrite(Self);
end;
FD_CLOSE :
begin
//由于服务器关闭,所以客户也要关闭套接字
Close;
if Assigned(FOnDisConnect) then FOnDisConnect(Self);
end;
end;//case
end;
//建立连接
procedure TIPXClientSocket.Open;
var
RemoteAddr : TSOCKADDR_IPX;
nRet : Integer;
begin
FActive := False;//是否建立连接的标志
if not CanUseWinsock then
begin
if Assigned(FOnError) then
FOnError(self, '不能初始化套接口');
Exit;
end;
Close;
if (Fsa_nodenum <> '') and
(Fsa_netnum <> '') and
(Fsa_socket <> '') then
begin
//远程主机IPX地址
RemoteAddr.sa_family := AF_IPX;
ATOH(Fsa_netnum, RemoteAddr.sa_netnum, 4);
ATOH(Fsa_socket, RemoteAddr.sa_socket, 2);
ATOH(Fsa_nodenum, RemoteAddr.sa_nodenum, 6);
//创建套接字
FSocket := socket(AF_IPX, SOCK_STREAM, NSPROTO_SPX);
if FSocket = INVALID_SOCKET then
begin
if Assigned(FOnError) then
FOnError(Self, '创建套接字失败');
Exit;
end;
//选定事件
nRet := WSAAsyncSelect(FSocket, Fwnd,
WM_SOCKET,FD_READ or FD_CONNECT
or FD_WRITE or FD_CLOSE);
if nRet = SOCKET_ERROR then
begin
if Assigned(FOnError) then
FOnError(Self, '选定事件失败');
if closesocket(FSocket) <> 0 then
if Assigned(FOnError) then
FOnError(self, '关闭套接字失败');
FSocket := INVALID_SOCKET;
Exit;
end;
//连接服务器
nRet := connect(FSocket, PSockAddr(@RemoteAddr), sizeof(TSOCKADDR_IPX));
if nRet = SOCKET_ERROR then
if WSAGetLastError <> WSAEWOULDBLOCK then
begin
if Assigned(FOnError) then
FOnError(Self,'连接到服务器失败');
Exit;
end;
FActive := True;
end else
begin
if Assigned(FOnError) then
FOnError(Self,'请输入完整的地址信息');
Exit;
end;
end;
//关闭套接字
procedure TIPXClientSocket.Close;
begin
if FSocket = INVALID_SOCKET then Exit;
if closesocket(FSocket) <> 0 then
if Assigned(FOnError) then
FOnError(self, '关闭套接字失败');
FSocket := INVALID_SOCKET;
end;
function TIPXClientSocket.SendBuf(var Buf; BufLen : Integer) : Integer;
var
nSend : Integer;
begin
Result := 0;
if FSocket = INVALID_SOCKET then
begin
if Assigned(FOnError) then
FOnError(self, '没有创建套接字,不能发送数据');
Exit;
end;
nSend := send(FSocket, Buf, BufLen, 0);
if nSend = SOCKET_ERROR then
begin
if WSAGetLastError <> WSAEWOULDBLOCK then
begin
if Assigned(FOnError) then
FOnError(self,'发送数据失败');
Exit;
end;
end;
Result := nSend;
end;
function TIPXClientSocket.RecvBuf(var Buf; BufLen : Integer) : Integer;
var
nRecv : Integer;
begin
Result := 0;
if FSocket = INVALID_SOCKET then
begin
if Assigned(FOnError) then
FOnError(self, '没有创建套接字,不能接收数据');
Exit;
end;
nRecv := recv(FSocket, Buf, BufLen, 0);
if nRecv < 0 then
begin
if WSAGetLastError <> WSAEWOULDBLOCK then
begin
if Assigned(FOnError) then
FOnError(self,'接收数据失败');
Exit;
end;
end;
Result := nRecv;
end;
{TIPXServerSocket}
constructor TIPXServerSocket.Create(AOwner : TComponent);
var
i : Integer;
begin
inherited;
//先分配5个
SetLength(FClientSocket, 5);
for i := 0 to 4 do
begin
FClientSocket[i] := INVALID_SOCKET;
end;
FListenSocket := INVALID_SOCKET;
FWnd := AllocateHWND(WndProc);
FActive := False;
FActiveClientCount := 0;
end;
destructor TIPXServerSocket.Destroy;
begin
Close;
DeallocateHWND(FWnd);
inherited;
end;
procedure TIPXServerSocket.WndProc(var Msg : TMessage);
var
i, j : Integer;
begin
//不是网络事件
if Msg.Msg <> WM_SOCKET_SERVER then
begin
Msg.Result := DefWindowProc(FWnd, Msg.Msg, Msg.WParam, Msg.LParam);
Exit;
end;
if WSAGetSelectError(Msg.LParam) <> 0 then
begin
if Assigned(FOnError) then
FOnError(Self,'网络事件消息出错');
Exit;
end;
case WSAGetSelectEvent(Msg.LParam) of
FD_ACCEPT :
begin
if AcceptConnect then
begin
if Assigned(FOnClientConnected) then
FOnClientConnected(Self, TSOCKET(MSG.WParam));
end
else
if Assigned(FOnError) then
FOnError(self, '接受客户端连接失败');
end;
FD_READ :
begin
if Assigned(FOnRead) then FOnRead(self, MSG.WParam);
end;
FD_WRITE :
begin
if Assigned(FOnWrite) then FOnWrite(Self, MSG.WParam);
end;
FD_CLOSE ://客户端关闭
begin
//定位客户套接字
for i := 0 to FActiveClientCount - 1 do
begin
if FClientSocket[i] = TSocket(MSG.WParam) then
break;
end;
if CloseSocket(FClientSocket[i]) <> 0 then
if Assigned(FOnError) then
FOnError(self, '关闭套接字失败');
//调整数组
for j := i + 1 to FActiveClientCount - 1 do
FClientSocket[j - 1] := FClientSocket[j];
FClientSocket[FActiveClientCount - 1] := INVALID_SOCKET;
//更改FActiveClientCount
FActiveClientCount := FActiveClientCount - 1;
if Assigned(FOnClientDisConnect) then
FOnClientDisConnect(self, TSocket(MSG.WParam));
end;
end;//case
end;
procedure TIPXServerSocket.Open;
var
nRet : Integer;
LocalAddr : TSOCKADDR_IPX;//本地主机地址
begin
FActive := False;
if not CanUseWinSock then
if Assigned(FOnError) then
FOnError(self, '不能使用套接口');
//创建侦听套接字
FListenSocket := socket(AF_IPX, SOCK_STREAM, NSPROTO_SPX);
if FListenSocket = INVALID_SOCKET then
begin
if Assigned(FOnError) then
FOnError(self, '创建侦听套接字失败');
Exit;
end;
//地址协议族
LocalAddr.sa_family := AF_IPX;
if Fsa_socket = '' then
begin
if Assigned(FOnError) then
FOnError(self, '请输入端口号');
Exit;
end;
//端口号
ATOH(Fsa_socket, LocalAddr.sa_socket, 2);
//给SOCKET绑上本地主机地址;
nRet := bind(FListenSocket, PSOCKADDR(@LocalAddr), sizeof(TSOCKADDR_IPX));
if nRet = SOCKET_ERROR then
begin
if Assigned(FOnError) then
FOnError(self, '绑定主机地址和端口失败');
if CloseSocket(FListenSocket) <> 0 then
if Assigned(FOnError) then
FOnError(self, '关闭套接字失败');
FListenSocket := INVALID_SOCKET;
Exit;
end;
//选择可以发生的事件
nRet := WSAAsyncSelect(FListenSocket, FWnd, WM_SOCKET_SERVER, FD_ACCEPT);
if nRet = SOCKET_ERROR then
begin
if Assigned(FOnError) then
FOnError(self, '选择事件失败');
if CloseSocket(FListenSocket) <> 0 then
if Assigned(FOnError) then
FOnError(self, '关闭套接字失败');
FListenSocket := INVALID_SOCKET;
Exit;
end;
//建立侦听
nRet := Listen(FListenSocket, SOMAXCONN);
if nRet = SOCKET_ERROR then
begin
if Assigned(FOnError) then
FOnError(self, '建立侦听失败');
if CloseSocket(FListenSocket) <>0 then
if Assigned(FOnError) then
FOnError(self, '关闭套接字失败');
FListenSocket := INVALID_SOCKET;
Exit;
end;
FActive := True;
if Assigned(FOnListen) then
FOnListen(self, FListenSocket);
end;
procedure TIPXServerSocket.Close;
var
i : Integer;
begin
//关闭所有套接字
for i := 0 to FActiveClientCount - 1 do
begin
if FClientSocket[i] <> INVALID_SOCKET then
begin
if CloseSocket(FClientSocket[i]) <> 0 then
if Assigned(FOnError) then
FOnError(self, '关闭套接字失败');
FClientSocket[i] := INVALID_SOCKET;
end;
end;
//关闭侦听套接字
if FListenSocket <> INVALID_SOCKET then
begin
if CloseSocket(FListenSocket) <> 0 then
if Assigned(FOnError) then
FOnError(self, '关闭套接字失败');
FlistenSocket := INVALID_SOCKET;
end;
end;
function TIPXServerSocket.AcceptConnect : Boolean;
var
nRet : Integer;
len : Integer;
ClientAddr : TSOCKADDR_IPX;//客户机地址
i, j : Integer;
begin
Result := False;
//如果还有空间
if FActiveClientCount <= High(FClientSocket) then
i := FActiveClientCount
else
begin
//再分配FClientSocket
SetLength(FClientSocket, High(FClientSocket) + 5);
for j := FActiveClientCount to High(FClientSocket) do
FClientSocket[j] := INVALID_SOCKET;
i := FActiveClientCount;
end;
//接受客户端连接
FClientSocket[i] := accept(FListenSocket, TSOCKADDR(ClientAddr), len);
if FClientSocket[i] = INVALID_SOCKET then
begin
if Assigned(FOnError) then
FOnError(self, '接受客户端连接失败');
if closesocket(FClientSocket[i]) <> 0 then
if Assigned(FOnError) then
FOnError(self, '关闭套接字失败');
FClientSocket[i] := INVALID_SOCKET;
Exit;
end;
//设定事件
nRet := WSAAsyncSelect(FClientSocket[i],FWnd ,WM_SOCKET_SERVER, FD_READ or FD_WRITE
or FD_CLOSE);
if nRet = SOCKET_ERROR then
begin
if Assigned(FOnError) then
FOnError(self, '设定事件失败');
if closesocket(FClientSocket[i]) <> 0 then
if Assigned(FOnError) then
FOnError(self, '关闭套接字失败');
FClientSocket[i] := INVALID_SOCKET;
Exit;
end;
//活动的客户端数
FActiveClientCount := FActiveClientCount + 1;
Result := True;
end;
function TIPXServerSocket.SendBuf(ClientSocket : TSocket; var Buf; BufLen : Integer) : Integer;
var
nSend : Integer;
begin
Result := 0;
if ClientSocket = INVALID_SOCKET then
begin
if Assigned(FOnError) then
FOnError(self, '没有创建套接字,不能发送数据');
Exit;
end;
nSend := send(ClientSocket, Buf, BufLen, 0);
if nSend = SOCKET_ERROR then
begin
if WSAGetLastError <> WSAEWOULDBLOCK then
begin
if Assigned(FOnError) then
FOnError(self,'发送数据失败');
Exit;
end;
end;
Result := nSend;
end;
function TIPXServerSocket.RecvBuf(ClientSocket : TSocket; var Buf; BufLen : Integer) : Integer;
var
nRecv : Integer;
begin
Result := 0;
if ClientSocket = INVALID_SOCKET then
begin
if Assigned(FOnError) then
FOnError(self, '没有创建套接字,不能接收数据');
Exit;
end;
nRecv := recv(ClientSocket, Buf, BufLen, 0);
if nRecv < 0 then
begin
if WSAGetLastError <> WSAEWOULDBLOCK then
begin
if Assigned(FOnError) then
FOnError(self,'接收数据失败');
Exit;
end;
end;
Result := nRecv;
end;
Initialization
WSAStartError := WSAStartUp(WINSOCK_VERSION, WSAData);
Finalization
WSACleanUp;
end.