使用Win32 API获得端口占用列表并关闭对应进程

获得端口占用可以用命令netstat -ab实现,结束进程可以用taskkill,但是用C++怎么实现呢?

答案是Windows已经提供了API实现功能。GetTcpTable可以得到当前占用的端口数量以及具体的ip和端口,但是得不到对应的进程。GetTcpTable2可以额外获得占用端口对应进程的pid,拿到pid,后面问题就好办了。

下面是效果图:
在这里插入图片描述

源码:

#define _WINSOCK_DEPRECATED_NO_WARNINGS

#include <iostream>
#include <ws2tcpip.h>

#include <Windows.h>
#include <psapi.h>//GetModuleFileNameEx

#include <iphlpapi.h>
#pragma comment(lib, "iphlpapi.lib")

#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")

using namespace std;

//查找占用网络端口的进程,返回进程的pid,没有则返回0
//throw: 内存不足抛出runtime_error
DWORD GetIdOfOccupiedPortProcess(unsigned short port)
{
	ULONG ulSize = sizeof(MIB_TCPTABLE2);
	PMIB_TCPTABLE2 pTcpTable = (PMIB_TCPTABLE2)malloc(ulSize);

	if (pTcpTable == nullptr)
		throw runtime_error("memory is not enough.");

	//调用GetTcpTable2,若内存不够,则释放,然后再申请
	if (GetTcpTable2(pTcpTable, &ulSize, TRUE) == ERROR_INSUFFICIENT_BUFFER)
	{
		free(pTcpTable);
		pTcpTable = (PMIB_TCPTABLE2)malloc(ulSize);
		if (pTcpTable == nullptr)
			throw runtime_error("memory is not enough.");
	}

	if (GetTcpTable2(pTcpTable, &ulSize, TRUE) == NO_ERROR)
	{
		//遍历端口
		cout << "start finding TCP port:" << endl;
		for (int i = 0; i < pTcpTable->dwNumEntries; ++i)
		{
			//得到端口号
			unsigned short localPort = ntohs((u_short)pTcpTable->table[i].dwLocalPort);
			cout << localPort << endl;

			//得到占用端口的进程pid
			auto pid = pTcpTable->table[i].dwOwningPid;

			//发现目标端口,返回pid
			if (port == localPort)
			{
				cout << "found the destination port." << endl;
				free(pTcpTable);
				return pid;
			}
		}
	}

	free(pTcpTable);
	return 0;
}

//提升权限为DEBUG,用于OpenProcess失败,GetLastError返回5“无权限操作错误”的情况
//应在调用OpenProcess之前调用
BOOL EnableDebugPrivilege()
{
	HANDLE hToken;
	BOOL fOk = FALSE;
	if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
	{
		TOKEN_PRIVILEGES tp;
		tp.PrivilegeCount = 1;
		LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid);

		tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
		AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);

		fOk = (GetLastError() == ERROR_SUCCESS);
		CloseHandle(hToken);
	}
	return fOk;
}

int main(int argc, char* argv[])
{
	//提升权限
	if (EnableDebugPrivilege() == FALSE)
	{
		cout << "Failure in EnableDebugPrivilege." << endl;
		return -1;
	}

	while (1)
	{
		//提示输入一个端口号
		unsigned short port;
		cout << "Please input a port number:";
		cin >> port;

		//找到占用端口的进程的pid
		auto pid = GetIdOfOccupiedPortProcess(port);
		if (pid == 0)
		{
			cout << "No program occupied port" << port << "." << endl;
			continue;
		}

		//打开进程
		HANDLE hProcess = ::OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION, FALSE, pid);
		if (hProcess == NULL)
		{
			//出错时报错
			DWORD errCode = GetLastError();
			cout << "Failed in OpenProcess. Error code: " << errCode << endl;
			system("pause");
			return -1;
		}

		//得到进程对应exe文件名
		TCHAR buf[MAX_PATH];
		if (GetModuleFileNameEx(hProcess, 0, buf, MAX_PATH))
		{
			cout << "Do you want to kill the process " << buf << "?(y/n)" << endl;
		}
		else
		{
			cout << "Failed in GetModuleFileNameEx. Maybe no authorization." << endl;
			system("pause");
			return -1;
		}

		char key;
		cin >> key;
		if (key == 'y')
		{
			if (::TerminateProcess(hProcess, 0))
			{
				cout << "Success." << endl;
			}
			else
			{
				cout << "Failed in TerminateProcess." << endl;
				system("pause");
				return -1;
			}
		}
		CloseHandle(hProcess);
	}

	return 0;
}

参考

GetTcpTable2 function
Build your own netstat.exe with c# | Tim Van Wassenhove’s blog
h

如何在C++中获取进程名?

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值