BDS(Delphi/C++Builder)当中的TServerSocket估计是基于简单应用或者学习的目的考虑,所以直接就绑定了泛地址,而不支持针对性的IP地址绑定,这也就使得一些特殊的环境让人感觉有点不舒服,甚至有点无奈。本文就简单介绍一种“曲线破解”法来解决这个绑定的问题。本示例主要是基于BDS2007的TServerSocket,其它版本请根据实际情况进行调整未必都能实现。
//基于继承
unit Unit3;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ScktComp, StdCtrls;
type
TForm3 = class(TForm)
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
ServerSOcket1: TServerSocket;
public
{ Public declarations }
end;
TMyServerSocket = class(TServerSocket)
private
FBindAddr: String;
protected
procedure ServerSocketEvent(Sender: TObject; Socket: TCustomWinSocket;
SocketEvent: TSocketEvent);
public
property Address: String read FBindAddr write FBindAddr;
constructor Create(AOwner: TComponent); override;
end;
var
Form3: TForm3;
implementation
Uses
Winsock;
{$R *.dfm}
function IsIP(const AHost:String):Boolean;
var
iLen
, I
, TestDigit
, DotCount: Integer;
TestIP: String;
begin
Result := false;
TestIP := AHost + '.';
iLen := Length(AHost);
if(iLen<7) or (iLen>15) then Exit;
TestDigit := 0;
DotCount := 0;
for i := 1 to iLen + 1 do
begin
if(((TestIP[i] < '0') or (TestIP[i] > '9')) and ((TestIP[i] <> '.') or (i = 0))) then Exit;
if(TestIP[i] = '.') then
begin
if (TestDigit > 255) or (TestDigit < 0) then Exit;
if(TestIP[i-1] = '.') then Exit;
TestDigit := 0;
Inc(DotCount);
end
else
TestDigit := TestDigit * 10 + (ord(TestIP[i]) - 48);
end;
Result := DotCount = 4;
end;
function LocalGetHostByName(const AHost:String):String;
var
WSAData: TWSADATA;
Host: PHostEnt;
begin
Result := AHost;
if(WSAStartup(MakeWord(1,1), WSAData) <> 0) then Exit;
try
Host := gethostbyname(PChar(AHost));
Result := IntToStr(Byte(Host^.h_addr_list^[0])) + '.' +
IntToStr(Byte(Host^.h_addr_list^[1])) + '.' +
IntToStr(Byte(Host^.h_addr_list^[2])) + '.' +
IntToStr(Byte(Host^.h_addr_list^[3]));
if Not IsIP(Result) then Result:=AHost;
finally
WSACleanup;
end;
end;
function ResolveHost(const AHost: String): String;
begin
Result := AHost;
if AnsiSameText(AHost, 'LOCALHOST') or AnsiSameText(AHost, '(Local)') or AnsiSameText(AHost, '.') then // this computer
Result := '127.0.0.1'
else if Not IsIP(Result) then
Result := LocalGetHostByName(AHost);
end;
constructor TMyServerSocket.Create(AOwner: TComponent);
begin
Inherited Create(AOwner);
FServerSocket.OnSocketEvent := ServerSocketEvent; //可以进行绑定的事件拦截事件
end;
//拦载LookupState为lsIdle状态,替代为 lsLookupAddress,从而使用自定义的地址替代原来的INADDR_ANY
procedure TMyServerSocket.ServerSocketEvent(Sender: TObject; Socket: TCustomWinSocket;
SocketEvent: TSocketEvent);
var
Addr: TSockAddrIn;
LookupState: TLookupState;
begin
case SocketEvent of
seLookup:
begin
if Socket.LookupState = lsIdle then
begin
if FBindAddr <> '' then //Hack Bind Address
begin
Addr := Socket.Addr;
Addr.sin_addr.S_addr := inet_addr(PChar(ResolveHost(FBindAddr)));
LookupState := lsLookupAddress;
move( PChar(@Addr)^,
PChar( Integer(Socket) //首地址 (4字节对齐)
+ sizeof(Pointer) //虚函数表
+ sizeof(TSocket)//FSocket: TSocket; //占4字节
+ sizeof(Pointer)//FConnected: Boolean; //注意字节对齐,由于后续为4字节,虽只有1字节,但是占4字节
+ sizeof(TStream)//FSendStream: TStream; //占4字节
+ sizeof(Pointer)//FDropAfterSend: Boolean; //注意字节对齐,由于后续为4字节,虽只有1字节,但是占4字节
+ sizeof(HWnd)//FHandle: HWnd; //占4字节
)^,
sizeof(TSockAddrIn));
move( PChar(@LookupState)^,
PChar( Integer(Socket)
+ sizeof(Pointer)
+ sizeof(TSocket)//FSocket: TSocket;
+ sizeof(Pointer)//FConnected: Boolean; //注意字节对齐
+ sizeof(TStream)//FSendStream: TStream;
+ sizeof(Pointer)//FDropAfterSend: Boolean; //注意字节对齐
+ sizeof(HWnd)//FHandle: HWnd;
+ sizeof(TSockAddrIn)//FAddr: TSockAddrIn;
+ sizeof(TASyncStyles)//FAsyncStyles: TASyncStyles; //注意字节对齐
)^,
sizeof(TLookupState));
end;
end;
end;
end;
Event(Socket,SocketEvent);
end;
procedure TForm3.Button1Click(Sender: TObject);
begin
ServerSocket1.Open;
end;
procedure TForm3.FormCreate(Sender: TObject);
begin
ServerSocket1 := TMyServerSocket.Create(self);
ServerSocket1.Port := 9999;
TMyServerSocket(ServerSocket1).Address := '192.168.10.121';
//事件处理过程列表
//ServerSocket1.OnAccept := ...
end;
procedure TForm3.FormDestroy(Sender: TObject);
begin
ServerSocket1.Free;
end;
end.