转自http://blog.csdn.net/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.csdn.net/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.csdn.net/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 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 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。