📄 scanport.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 + -