⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 scanport.pas

📁 多线程端口扫描演示
💻 PAS
字号:
//------------------------------------------------------------------------------
//  多线程端口扫描单元ScanPort.pas(仅作为教学演示使用)
//  功能:实现(1)辅助类TConnectThread连接线程类,用于控制连接超时
//            (2)TScanPortThread类,端口扫描线程类,用于单个端口扫描
//            (3)TScanHostThread类,主机扫描线程类,用于单个主机扫描
//  作者:邓飞 2004.7. 成都理工大学--计算机工程系
//  **声明:任何个人和团体均可以使用该单元文件,但需要保留原作者姓名**
//------------------------------------------------------------------------------
unit ScanPort;

interface

uses
  Classes, Windows, SysUtils, IdWinSock2, ComCtrls;

type
  TIPArray = Array[0..3] of Byte;         //IP地址数组类型
  PIPArray = ^TIPArray;
  
  TPortStateArray = Array of Boolean;     //端口状态数组类型

  TScanPortThread = class(TThread)        //端口扫描线程
  protected
    FHostIP: DWord;                       //主机IP
    FPort: Integer;                       //端口列表
    FPortIndex: Integer;                  //端口下标
    FOutTime: Integer;                    //最大扫描时间
    FConnected: Boolean;                  //是否能够连接
  protected
    procedure ScanPort;                   //扫描端口
  public
    constructor Create; reintroduce;
    destructor Destroy; override;

    procedure Execute; override;
  end;

  TScanPortThreads = Array of TScanPortThread;    //扫描端口线程组

  TScanHostThread = class(TThread)                //扫描主机线程类
  protected
    FHostIP: DWord;                               //主机IP
    FPortList: TList;                             //端口列表
    FOutTime: Integer;                            //端口最大扫描时间
    FPortStateArray: TPortStateArray;             //端口状态数组
    FThreadCount: Integer;                        //线程数量
    FThreads: TScanPortThreads;                   //线程数组
    FNext: Integer;                               //下一个扫描端口下标
    FScanCount: Integer;                          //已经扫描的端口数量
    FProgressBar: TProgressBar;                   //扫描进度杆

  protected
    function GetPortState(Index: Integer): Boolean;
    procedure UpdateProgressBar;                  //更新进度条
  public
    constructor Create; reintroduce;
    destructor Destroy; override;

    procedure SetHostIP(IPWord: DWord);           //设置主机IP
    procedure SetPortList(PortList: TList);       //设置扫描端口列表
    procedure SetThreadCount(Count: Integer);     //设置最大同时启动的线程数量

    procedure Execute; override;

  public
    property OutTime: Integer read FOutTime write FOutTime;    //连接超时属性
    property PortStates[Index: Integer]: Boolean read GetPortState; default; //端口状态属性(True开放)
    property ProgressBar: TProgressBar read FProgressBar write FProgressBar; //进度条属性
    property Terminated;                                                     //线程是否终止/完成属性
  end;

  //将字符串表示的IP地址转换成长度为4的字节数组,如果成功转换返回True
  function DecodeIPStrToArray(IPStr: String; var IPArray: TIPArray): Boolean;
  //将4字节数组表示的IP地址转换成字符串
  procedure DecodeIPArrayToStr(const IPArray: TIPArray; var IPStr: String);
  //将4字节表示的IP地址转换成32位的双字节类型
  procedure DecodeIPArrayToDWord(const IPArray: TIPArray; var IPWord: DWord);
  //将用双字节表示的IP地址转换成4字节数组
  procedure DecodeIPDWordToArray(IPWord: DWord; var IPArray: TIPArray);


implementation

type
  TConnectThread = class(TThread) //连接线程类,用于连接超时
  protected
    FConnected: Boolean;
    FSocket: TSocket;
    FSockAddrIn: TSockAddrIn;
  public
    constructor Create; reintroduce;
    procedure Execute; override;
  end;

//----全局工具函数--------------------------------------------------------------
function DecodeIPStrToArray(IPStr: String; var IPArray: TIPArray): Boolean;
var
  i, j, k, Pos, V: Integer;
  TmpStr: String;
begin
  Result:= False;
  SetLength(TmpStr, 3);
  ZeroMemory(PChar(TmpStr), 4);

  i:= 1;    j:= 0;    k:= 0;    Pos:= 0;
  while i <= Integer(StrLen(PChar(IPStr))) do begin
    if IPStr[i] <> '.' then begin
      Inc(j);
      if j > 3 then Exit; //非法的IP地址(其中一组超长)
      TmpStr[i-Pos]:= IPStr[i];
    end
    else begin
      V:= StrToIntDef(TmpStr, -1);
      if (V < 0) and (V > 255) then Exit; //非法的IP地址(其中一组无法转化为整数)
      IPArray[k]:= V;
      ZeroMemory(PChar(TmpStr), 4);
      j:= 0;   Inc(k);   Pos:= i;
    end;
    Inc(i);
  end;

  V:= StrToIntDef(TmpStr, -1);
  if (V < 0) and (V > 255) then Exit; //非法的IP地址(最后一组无法转化为整数)
  IPArray[k]:= V;
  Result:= True;
end;

procedure DecodeIPArrayToStr(const IPArray: TIPArray; var IPStr: String);
begin
  IPStr:= IntToStr(IPArray[0])+'.'+IntToStr(IPArray[1])+'.'+
          IntToStr(IPArray[2])+'.'+IntToStr(IPArray[3]);
end;

procedure DecodeIPArrayToDWord(const IPArray: TIPArray; var IPWord: DWord);
var
  i: Integer;
begin
  IPWord:= 0;
  //通过移位和按位or操作将IP数组合成一个32位的DWord数
  //规则为:[127][0][0][1]  (127 shl 24) or (0 shl 16) or (0 shl 8) or 1
  for i:=0 to 3 do begin
    IPWord:= IPWord or (DWord(IPArray[i]) shl ((3-i)*8));
  end;
end;

procedure DecodeIPDWordToArray(IPWord: DWord; var IPArray: TIPArray);
var
  i: Integer;
begin
  //通过移位和按位and操作将32位的DWord地址转换成4字节数组
  //规则为:((IPWord shr 24) and $ff) and ((IPWord shl 16) and $ff) or
  //        ((IPWord shl 8) and $ff) and ((IPWord shl 0) and $ff)
  for i:=0 to 3 do begin
    IPArray[i]:= Byte((IPWord shr ((3-i)*8)) and $000000ff);
  end;
end;

//----class TConnectThread------------------------------------------------------
constructor TConnectThread.Create;
begin
  FConnected:= False;
  inherited Create(True);
  Priority:= tpHighest;   //将连接线程的优先级设为最高
end;

procedure TConnectThread.Execute;
begin
  FConnected:= False;   //连接标志置为False
  if connect(FSocket, @FSockAddrIn, Sizeof(FSockAddrIn)) <> SOCKET_ERROR then begin  //执行连接
    FConnected:= True;
  end;
  Terminate;   //设置线程终止标志
end;

//----class TScanPortThread-----------------------------------------------------
constructor TScanPortThread.Create;
begin
  inherited Create(TRUE);
  FHostIP:= 0;
  FOutTime:= 5000;
  FConnected:= False;
  Priority:= tpHigher;    //将端口扫描的优先级设为高次于连接线程
end;

destructor TScanPortThread.Destroy;
begin
end;

procedure TScanPortThread.Execute;
begin
  FConnected:= False;
  ScanPort;
  Terminate;
end;

procedure TScanPortThread.ScanPort;
var
  IPArray: TIPArray;
  OutTime, LSleepTime: Integer;
  ConnectThread: TConnectThread;
begin
  LSleepTime:= 25;
  ConnectThread:= TConnectThread.Create;
  DecodeIPDWordToArray(FHostIP, IPArray);
  ConnectThread.FSockAddrIn.sin_addr.S_un_b.s_b1:= IPArray[0];
  ConnectThread.FSockAddrIn.sin_addr.S_un_b.s_b2:= IPArray[1];
  ConnectThread.FSockAddrIn.sin_addr.S_un_b.s_b3:= IPArray[2];
  ConnectThread.FSockAddrIn.sin_addr.S_un_b.s_b4:= IPArray[3];
  ConnectThread.FSockAddrIn.sin_family := AF_INET;
  ConnectThread.FSockAddrIn.sin_port := HToNS(FPort);

  ConnectThread.FSocket:= Socket(AF_INET, SOCK_STREAM, 0); //创建Socket
  ConnectThread.Resume;                                    //启动连接线程

  OutTime:= FOutTime;
  while OutTime > 0 do begin
    //(1)检查用户是否终止线程
    if Terminated then begin
      FConnected:= ConnectThread.FConnected;
      ConnectThread.FreeOnTerminate:= True;
      if not ConnectThread.Terminated then ConnectThread.Terminate;
      CloseSocket(ConnectThread.FSocket);     //关闭Socket连接
      Exit;
    end;
    //(2)延时等待
    if OutTime > LSleepTime then begin  //如果剩余时间大于睡眠时间片
      Sleep(LSleepTime);
      OutTime:= OutTime-LSleepTime;
    end
    else begin
      Sleep(OutTime);
      OutTime:= 0;
    end;
    //(3)检查连接线程是否运行完毕
    if ConnectThread.Terminated then begin
      FConnected:= ConnectThread.FConnected;  //获取结果
      CloseSocket(ConnectThread.FSocket);     //关闭Socket连接
      ConnectThread.Free;                     //释放连接线程资源
      Terminate;                              //设置线程终止标志
      Exit;
    end;
  end;
  //超时终止处理
  CloseSocket(ConnectThread.FSocket);   //关闭Socket连接
  FConnected:= False;                   //连接端口失败
  ConnectThread.FreeOnTerminate:= True;
  if not ConnectThread.Terminated then ConnectThread.Terminate;
  Terminate;                            //设置线程终止标志
end;

//----class TScanHostThread-----------------------------------------------------
constructor TScanHostThread.Create;
var
  i: Integer;
begin
  FHostIP:= 0;
  FPortList:= nil;
  FOutTime:= 5000;
  FThreadCount:= 30;
  SetLength(FThreads, FThreadCount);
  for i:=0 to FThreadCount-1 do FThreads[i]:= nil;
  FNext:= 0;
  FProgressBar:= nil;
  inherited Create(True);
end;

destructor TScanHostThread.Destroy;
begin
end;

function TScanHostThread.GetPortState(Index: Integer): Boolean;
begin
  if (Index >= 0) and (Index < FPortList.Count) then
    Result:= FPortStateArray[Index]
  else Result:= False;
end;

procedure TScanHostThread.SetHostIP(IPWord: DWord);
begin
  FHostIP:= IPWord;
end;

procedure TScanHostThread.SetPortList(PortList: TList);
begin
  FPortList:= PortList;
  SetLength(FPortStateArray, PortList.Count);
end;

procedure TScanHostThread.SetThreadCount(Count: Integer);
var
  i: Integer;
begin
  for i:=0 to FThreadCount-1 do begin
    if FThreads[i] <> nil then FThreads[i].Free;
  end;
  FThreadCount:= Count;
  SetLength(FThreads, Count);
  for i:=0 to FThreadCount-1 do FThreads[i]:= nil;
end;

procedure TScanHostThread.UpdateProgressBar;
begin
  if FProgressBar <> nil then FProgressBar.Position:= FScanCount;
end;

procedure TScanHostThread.Execute;
var
  i: Integer;
begin
  FNext:= 0;    FScanCount:= 0;
  if FProgressBar <> nil then begin
    FProgressBar.Max:= FPortList.Count;
    FProgressBar.Position:= 0;
  end;
  while not Terminated do begin
    Sleep(75);  //睡眠一段时间以后再进行检查工作,这样可以避免连续循环让CPU满负荷工作

    //(1)检查是否有完成的线程,如果有记录结果,释放线程
    for i:=0 to FThreadCount-1 do begin
      if (FThreads[i] <> nil) and FThreads[i].Terminated then begin
        FPortStateArray[FThreads[i].FPortIndex]:= FThreads[i].FConnected;
        Inc(FScanCount);
        Synchronize(UpdateProgressBar);
        FThreads[i].Free;
        FThreads[i]:= nil;
      end;
    end;

    //(2)检查是否已经扫描完成,如果是,跳出循环
    if FScanCount = FPortList.Count then Break;

    //(3)检查空闲的线程,根据需要分配新的任务
    for i:=0 to FThreadCount-1 do begin
      if (FThreads[i] = nil) and (FNext < FPortList.Count) then begin
        FThreads[i]:= TScanPortThread.Create;
        FThreads[i].FHostIP:= FHostIP;
        FThreads[i].FPort:= PInteger(FPortList.Items[FNext])^;
        FThreads[i].FPortIndex:= FNext;
        Inc(FNext);
        FThreads[i].FOutTime:= FOutTime;
        FThreads[i].Resume;
      end;
    end;
  end;

  if Terminated then begin    //用户终止线程,扫描未完成,删除所有正在工作的线程
    for i:=0 to FThreadCount-1 do begin
      if FThreads[i] <> nil then begin
        FThreads[i].FreeOnTerminate:= True;
        if not FThreads[i].Terminated then FThreads[i].Terminate;
        FThreads[i]:= nil;
      end;
    end;
  end;

  FProgressBar.Position:= 0;
  Terminate;
end;

end.

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -