转自http://blog.youkuaiyun.com/tht2009/article/details/40458425
在开发联网应用时,常常需要申请、绑定端口,这时就需判断哪些端口可用或指定端口是否被占用。在命令行窗口下,输入“netstat -ano"命令可以显示查看当前端口占用情况。如何在程序代码中实现这个功能呢?
当然也可以执行cmd命令,通过分析返回文本来判断。其实,Windows已经提供了获取当前网络连接状态的API,这些API都位于动态库Iphlpapi.dll中。跟查看端口情况相关的API主要有GetTcpTable、GetUdpTable、GetExtendedTcpTable、GetExtendedUdpTable,这四个API可以获取当前系统TCP、UDP端口连接表,后两个分别是前两个的扩展版,可以获得当前端口的占用进程ID。
1、GetExtendedTcpTable
函数原型如下:
DWORD GetExtendedTcpTable(
_Out_ PVOID pTcpTable,
_Inout_ PDWORD pdwSize,
_In_ BOOL bOrder,
_In_ ULONG ulAf,
_In_ TCP_TABLE_CLASS TableClass,
_In_ ULONG Reserved
);
参数:
pTcpTable(指针类型): 存储TCP端口连接信息表,具体结构类型根据ulAF和TableClass参数而定。
pdwSize(传址):pTcpTable指向的内存大小,如果pdwSize比需要的空间小,函数返回一个ERROR_INSUFFICIENT_BUFFER错误,并将pdwSize 置为所需大小。
bOrder(布尔): 返回结果是否排序。
ulAF(整数):IP类型。AF_INET——IPv4; AF_INET6——IPv6。
TableClass(枚举): TCP_TABLE_CLASS 。根据ulAF,传入相应不同值,返回的pTcpTable中结构类型不同。
typedef enum {
TCP_TABLE_BASIC_LISTENER,
TCP_TABLE_BASIC_CONNECTIONS,
TCP_TABLE_BASIC_ALL,
TCP_TABLE_OWNER_PID_LISTENER,
TCP_TABLE_OWNER_PID_CONNECTIONS,
TCP_TABLE_OWNER_PID_ALL,
TCP_TABLE_OWNER_MODULE_LISTENER,
TCP_TABLE_OWNER_MODULE_CONNECTIONS,
TCP_TABLE_OWNER_MODULE_ALL
} TCP_TABLE_CLASS, *PTCP_TABLE_CLASS;
Reserved(保留):设为0。
转自http://blog.youkuaiyun.com/tht2009/article/details/40458425
pTcpTable指向结构类型与ulAF和TableClass取值关系如下:
ulAF value | TableClass value | pTcpTable structure |
AF_INET | TCP_TABLE_BASIC_ALL | MIB_TCPTABLE |
TCP_TABLE_BASIC_CONNECTIONS | MIB_TCPTABLE | |
TCP_TABLE_BASIC_LISTENER | MIB_TCPTABLE | |
TCP_TABLE_OWNER_MODULE_ALL | MIB_TCPTABLE_OWNER_MODULE | |
TCP_TABLE_OWNER_MODULE_CONNECTIONS | MIB_TCPTABLE_OWNER_MODULE | |
TCP_TABLE_OWNER_MODULE_LISTENER | MIB_TCPTABLE_OWNER_MODULE | |
TCP_TABLE_OWNER_PID_ALL | MIB_TCPTABLE_OWNER_PID | |
TCP_TABLE_OWNER_PID_CONNECTIONS | MIB_TCPTABLE_OWNER_PID | |
TCP_TABLE_OWNER_PID_LISTENER | MIB_TCPTABLE_OWNER_PID | |
AF_INET6 | TCP_TABLE_OWNER_MODULE_ALL | MIB_TCP6TABLE_OWNER_MODULE |
TCP_TABLE_OWNER_MODULE_CONNECTIONS | MIB_TCP6TABLE_OWNER_MODULE | |
TCP_TABLE_OWNER_MODULE_LISTENER | MIB_TCP6TABLE_OWNER_MODULE | |
TCP_TABLE_OWNER_PID_ALL | MIB_TCP6TABLE_OWNER_PID | |
TCP_TABLE_OWNER_PID_CONNECTIONS | MIB_TCP6TABLE_OWNER_PID | |
TCP_TABLE_OWNER_PID_LISTENER | MIB_TCP6TABLE_OWNER_PID |
2、GetExtendedUdpTable
函数原型如下:
DWORD GetExtendedUdpTable(
_Out_ PVOID pUdpTable,
_Inout_ PDWORD pdwSize,
_In_ BOOL bOrder,
_In_ ULONG ulAf,
_In_ UDP_TABLE_CLASS TableClass,
_In_ ULONG Reserved
);
参数:
pUdpTable(指针类型): 存储UDP端口连接信息表,具体结构类型根据ulAF和TableClass参数而定。
pdwSize、bOrder、ulAF、Reserved:同TCP部分描述。
TableClass(枚举): UDP_TABLE_CLASS 。根据ulAF,传入相应不同值,返回的pUdpTable中结构类型不同。
typedef enum {
UDP_TABLE_BASIC,
UDP_TABLE_OWNER_PID,
UDP_TABLE_OWNER_MODULE
} UDP_TABLE_CLASS, *PUDP_TABLE_CLASS;
pUdpTable指向结构类型与ulAF和TableClass取值关系如下:
ulAF value | TableClass value | pUdpTable structure |
AF_INET | UDP_TABLE_BASIC | MIB_UDPTABLE |
UDP_TABLE_OWNER_MODULE | MIB_UDPTABLE_OWNER_MODULE | |
UDP_TABLE_OWNER_PID | MIB_UDPTABLE_OWNER_PID | |
AF_INET6 | UDP_TABLE_BASIC | MIB_UDP6TABLE |
UDP_TABLE_OWNER_MODULE | MIB_UDP6TABLE_OWNER_MODULE | |
UDP_TABLE_OWNER_PID | MIB_UDP6TABLE_OWNER_PID |
根据上面介绍,可以很容易写出查看占用端口的进程功能:FindPidByTcpPort和FindPidByUdpPort函数。主要Delphi实现代码如下:
/// <summary>通过指定TCP端口查找占用进程PID</summary>
/// <param name="port :Integer">TCP端口号</param>
/// <returns>Integer: -1,执行出错;0,指定端口未被占用;>0,占用进程PID</returns>
function FindPidByTcpPort(port: Cardinal): Integer;
/// <summary>通过指定UDP端口查找占用进程PID</summary>
/// <param name="port :Integer">UDP端口号</param>
/// <returns>Integer: -1,执行出错;0,指定端口未被占用;>0,占用进程PID</returns>
function FindPidByUdpPort(port: Cardinal): Integer;
转自http://blog.youkuaiyun.com/tht2009/article/details/40458425
function FindPidByTcpPort(port: Cardinal): Integer;
var
pTcpTable: PMibTcpTableOwnerPID;
dwSize: DWORD;
i: Integer;
begin
Result := -1;
dwSize := 0;
//查询大小
if GetExtendedTcpTable(nil, dwSize, FALSE, AF_INET, TCP_TABLE_OWNER_PID_ALL, 0)
= ERROR_INSUFFICIENT_BUFFER then
begin
pTcpTable := AllocMem(dwSize);
//获取TCP连接表
if GetExtendedTcpTable(pTcpTable, dwSize, True, AF_INET, TCP_TABLE_OWNER_PID_ALL, 0)
=NO_ERROR then
begin
port := htons(port);
for I := 0 to pTcpTable.dwNumEntries-1 do
begin
if pTcpTable.table[i].dwLocalPort = Port then
begin
Result := pTcpTable.table[i].dwOwningPid;
Break;
end;
end;
if Result<0 then
Result := 0;
end;
FreeMem(pTcpTable);
end;
end;
function FindPidByUdpPort(port: Cardinal): Integer;
var
pUdpTable: PMibUdpTableOwnerPID;
dwSize: DWORD;
i: Integer;
begin
Result := -1;
dwSize := 0;
//查询大小
if GetExtendedUdpTable(nil, dwSize, FALSE, AF_INET, UDP_TABLE_OWNER_PID, 0)
= ERROR_INSUFFICIENT_BUFFER then
begin
pUdpTable := AllocMem(dwSize);
//获取UDP连接表
if GetExtendedUdpTable(pUdpTable, dwSize, True, AF_INET, UDP_TABLE_OWNER_PID, 0)
=NO_ERROR then
begin
port := htons(port);
for I := 0 to pUdpTable.dwNumEntries-1 do
begin
if pUdpTable.table[i].dwLocalPort = Port then
begin
Result := pUdpTable.table[i].dwOwningPid;
Break;
end;
end;
if Result<0 then
Result := 0;
end;
FreeMem(pUdpTable);
end;
end;
其中声明这两个函数,定义MibUdpTableOwnerPID与MibTcpTableOwnerPID结构体部分请参照MSDN中介绍,也可下载完整代码IpHlpApi2.pas查阅:http://www.colafile.com/file/2413129。
转自http://blog.youkuaiyun.com/tht2009/article/details/40458425