如何通过C++ WinAPI获取本地TCP客户端连接的进程名称
获取本地TCP连接发起进程名称的WinAPI实现方法
要在Windows下通过C++ WinAPI获取本地TCP客户端连接的发起进程名称,可按以下步骤实现:
核心逻辑
- 从已接受的客户端套接字中提取本地IP/端口和远程IP/端口四元组信息
- 查询系统TCP连接表,匹配四元组得到对应进程ID(PID)
- 通过PID打开进程,读取进程可执行文件路径并提取名称
依赖头文件与库
除原有WinSock相关头文件外,还需添加:
#include <iphlpapi.h> #include <psapi.h> #include <shlwapi.h> #pragma comment(lib, "Iphlpapi.lib") #pragma comment(lib, "Psapi.lib") #pragma comment(lib, "Shlwapi.lib")
修改后的完整服务器代码
#include <winsock2.h> #include <ws2tcpip.h> #include <iostream> #include <string> #include <iphlpapi.h> #include <psapi.h> #include <shlwapi.h> #pragma comment(lib, "Ws2_32.lib") #pragma comment(lib, "Iphlpapi.lib") #pragma comment(lib, "Psapi.lib") #pragma comment(lib, "Shlwapi.lib") // 根据套接字获取连接进程名称 std::string GetClientProcessName(SOCKET clientSocket) { // 获取本地地址和远程地址 sockaddr_in localAddr{}, remoteAddr{}; int addrLen = sizeof(localAddr); if (getsockname(clientSocket, (sockaddr*)&localAddr, &addrLen) == SOCKET_ERROR) { return "Failed to get local address"; } addrLen = sizeof(remoteAddr); if (getpeername(clientSocket, (sockaddr*)&remoteAddr, &addrLen) == SOCKET_ERROR) { return "Failed to get remote address"; } // 查询TCP连接表 DWORD tableSize = 0; GetExtendedTcpTable(nullptr, &tableSize, FALSE, AF_INET, TCP_TABLE_OWNER_PID_ALL, 0); auto tcpTable = (PMIB_TCPTABLE_OWNER_PID)malloc(tableSize); if (!tcpTable) { return "Failed to allocate memory for TCP table"; } if (GetExtendedTcpTable(tcpTable, &tableSize, FALSE, AF_INET, TCP_TABLE_OWNER_PID_ALL, 0) != NO_ERROR) { free(tcpTable); return "Failed to get TCP table"; } // 遍历连接表匹配四元组 DWORD clientPID = 0; for (DWORD i = 0; i < tcpTable->dwNumEntries; ++i) { auto& entry = tcpTable->table[i]; // 匹配本地端口、远程端口、本地IP、远程IP if (entry.dwLocalPort == ntohs(localAddr.sin_port) && entry.dwRemotePort == ntohs(remoteAddr.sin_port) && entry.dwLocalAddr == localAddr.sin_addr.s_addr && entry.dwRemoteAddr == remoteAddr.sin_addr.s_addr && entry.dwState == MIB_TCP_STATE_ESTABLISHED) { clientPID = entry.dwOwningPid; break; } } free(tcpTable); if (clientPID == 0) { return "No matching process found"; } // 通过PID获取进程名称 HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, clientPID); if (!hProcess) { return "Failed to open process"; } char processPath[MAX_PATH] = {0}; GetModuleFileNameExA(hProcess, nullptr, processPath, MAX_PATH); CloseHandle(hProcess); // 提取文件名(进程名称) return PathFindFileNameA(processPath); } int main() { WSADATA wsaData; SOCKET listenSocket = INVALID_SOCKET; SOCKET clientSocket = INVALID_SOCKET; sockaddr_in serverAddr; // 初始化WinSock if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { std::cerr << "WSAStartup失败: " << WSAGetLastError() << std::endl; return 1; } // 创建套接字 listenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (listenSocket == INVALID_SOCKET) { std::cerr << "套接字创建失败: " << WSAGetLastError() << std::endl; WSACleanup(); return 1; } // 设置服务器地址 serverAddr.sin_family = AF_INET; serverAddr.sin_addr.s_addr = INADDR_ANY; serverAddr.sin_port = htons(8282); // 绑定套接字 if (bind(listenSocket, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) { std::cerr << "绑定失败: " << WSAGetLastError() << std::endl; closesocket(listenSocket); WSACleanup(); return 1; } // 监听连接 if (listen(listenSocket, SOMAXCONN) == SOCKET_ERROR) { std::cerr << "监听失败: " << WSAGetLastError() << std::endl; closesocket(listenSocket); WSACleanup(); return 1; } std::cout << "等待端口8282上的连接..." << std::endl; // 接受连接 clientSocket = accept(listenSocket, nullptr, nullptr); if (clientSocket == INVALID_SOCKET) { std::cerr << "接受连接失败: " << WSAGetLastError() << std::endl; closesocket(listenSocket); WSACleanup(); return 1; } // 获取客户端进程名称 std::string processName = GetClientProcessName(clientSocket); std::cout << "客户端进程名称: " << processName << std::endl; // 接收请求 char buffer[1024] = { 0 }; recv(clientSocket, buffer, sizeof(buffer) - 1, 0); std::cout << "收到请求:\n" << buffer << std::endl; // 发送响应 const char* response = "data"; send(clientSocket, response, strlen(response), 0); std::cout << "响应已发送。" << std::endl; // 清理资源 closesocket(clientSocket); closesocket(listenSocket); WSACleanup(); return 0; }
关键说明
- TCP连接表查询:使用
GetExtendedTcpTable获取包含PID的TCP连接信息,需先调用一次获取所需内存大小,再分配内存查询 - 进程权限:
OpenProcess需要PROCESS_QUERY_INFORMATION和PROCESS_VM_READ权限,若目标进程是系统进程或高权限进程,当前程序需以管理员权限运行 - 兼容性:代码适配IPv4,若需支持IPv6,需修改为
AF_INET6并使用对应MIB结构体
内容的提问来源于stack exchange,提问作者Joe J
相关产品推荐
相关产品推荐

