对TServerSocket实行IP绑定之一(辗转方式,只用于演示,除非必要不推荐使用)

来源:互联网 发布:索尼爱立信w980 软件 编辑:程序博客网 时间:2024/04/29 06:58

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;
    ServerSocket1: TServerSocket;
   
procedure FormCreate(Sender: TObject);
   
procedure Button1Click(Sender: TObject);

  private
    FBindAddr: String;
    OldSocketEvent: TSocketEventEvent;
   
procedure ServerSocketEvent(Sender: TObject; Socket: TCustomWinSocket;
    SocketEvent: TSocketEvent);
  public
   
{ Public declarations }
    property Address: String read FBindAddr write FBindAddr;
 
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;



//拦载LookupState为lsIdle状态,替代为 lsLookupAddress,从而使用自定义的地址替代原来的INADDR_ANY
procedure TForm3.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;
  OldSocketEvent(Sender,Socket,SocketEvent);
end;

procedure TForm3.Button1Click(Sender: TObject);
begin
  ServerSocket1.Open;
end;

procedure TForm3.FormCreate(Sender: TObject);
begin
  ServerSocket1.Port :
= 9999;
  Address :
= '192.168.10.121';
  OldSocketEvent :
= ServerSocket1.Socket.OnSocketEvent;
  ServerSocket1.Socket.OnSocketEvent :
= ServerSocketEvent;
 
if ServerSocket1.Active then
   
begin
      ServerSocket1.Close;
      ServerSocket1.Active :
= False;
   
end;
end;
end.

原创粉丝点击