#include <iostream>
#include <vector>
#include <unordered_map>
#include <thread>
#include <mutex>
#include <atomic>
#include <chrono>
#include <iomanip>   // 用于格式化输出
#include <winsock2.h>
#include <iphlpapi.h>
#include <pcap.h>
#include <ws2tcpip.h>
#include <psapi.h>

#pragma comment(lib, "wpcap.lib")
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "iphlpapi.lib")
#pragma comment(lib, "Psapi.lib")
// 协议常量
#define IPPROTO_TCP 6
#define IPPROTO_UDP 17

// IP头结构
typedef struct ip_hdr {
    unsigned char  ip_verlen;        // 版本(4) + 头长度(4)
    unsigned char  ip_tos;            // 服务类型
    unsigned short ip_len;            // 总长度
    unsigned short ip_id;             // 标识
    unsigned short ip_off;            // 分片偏移
    unsigned char  ip_ttl;            // 生存时间
    unsigned char  ip_p;              // 协议
    unsigned short ip_sum;            // 校验和
    struct in_addr ip_src;            // 源IP
    struct in_addr ip_dst;            // 目的IP
} IP_HDR;

// TCP头结构
typedef struct tcp_hdr {
    unsigned short th_sport;          // 源端口
    unsigned short th_dport;          // 目的端口
    unsigned int   th_seq;            // 序列号
    unsigned int   th_ack;            // 确认号
    unsigned char  th_lenres;         // 头长度和保留位
    unsigned char  th_flags;          // 标志
    unsigned short th_win;            // 窗口
    unsigned short th_sum;            // 校验和
    unsigned short th_urp;            // 紧急指针
} TCP_HDR;

// UDP头结构
typedef struct udp_hdr {
    unsigned short uh_sport;          // 源端口
    unsigned short uh_dport;          // 目的端口
    unsigned short uh_len;            // 长度
    unsigned short uh_sum;            // 校验和
} UDP_HDR;

// 每个进程的流量统计
struct ProcessStats {
    unsigned long long upload = 0;
    unsigned long long download = 0;
    bool is_start = false;
    std::string name;
};

// 全局数据
std::unordered_map<DWORD, ProcessStats> g_processStats;      // PID -> 统计
std::mutex g_statsMutex;

std::unordered_map<UINT64, DWORD> g_connectionMap;           // 连接标识 -> PID
std::mutex g_connMutex;

std::vector<in_addr> g_localIPs;                              // 本机IP列表

std::atomic<unsigned long long> g_totalUpload{ 0 };            // 总上传字节
std::atomic<unsigned long long> g_totalDownload{ 0 };          // 总下载字节

// 函数声明
void GetLocalIPs();
void UpdateConnectionTable();
DWORD LookupProcess(const IP_HDR* ip, const void* transport, bool isTcp);
bool IsLocalIP(const in_addr& ip);
void PacketHandler(u_char* user, const struct pcap_pkthdr* pkthdr, const u_char* packet);
void DisplayThread();

// 生成连接键(64位,用于快速查找)
UINT64 MakeKey(DWORD localAddr, WORD localPort, DWORD remoteAddr, WORD remotePort, BYTE proto) {
    UINT64 key = localAddr;
    key = (key << 16) | localPort;
    key = (key << 16) | remotePort;
    key = (key << 8) | proto;
    return key;
}

int main() {
    // 初始化Winsock
    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        std::cerr << "WSAStartup failed" << std::endl;
        return 1;
    }

    // 获取本机IP地址
    GetLocalIPs();

    // 查找网络设备
    pcap_if_t* alldevs;
    char errbuf[PCAP_ERRBUF_SIZE];
    if (pcap_findalldevs(&alldevs, errbuf) == -1) {
        std::cerr << "pcap_findalldevs: " << errbuf << std::endl;
        WSACleanup();
        return 1;
    }

    // 选择第一个可用设备(可根据需要改为用户选择)
    pcap_if_t* device = alldevs;
    if (!device) {
        std::cerr << "No network devices found." << std::endl;
        pcap_freealldevs(alldevs);
        WSACleanup();
        return 1;
    }

    // 打开设备
    pcap_t* handle = pcap_open_live(device->name, 65536, 1, 1000, errbuf);
    if (!handle) {
        std::cerr << "pcap_open_live: " << errbuf << std::endl;
        pcap_freealldevs(alldevs);
        WSACleanup();
        return 1;
    }

    // 编译过滤规则:只捕获IP数据包(IPv4)
    struct bpf_program fp;
    if (pcap_compile(handle, &fp, "ip", 0, PCAP_NETMASK_UNKNOWN) == -1) {
        std::cerr << "pcap_compile: " << pcap_geterr(handle) << std::endl;
        pcap_close(handle);
        pcap_freealldevs(alldevs);
        WSACleanup();
        return 1;
    }
    if (pcap_setfilter(handle, &fp) == -1) {
        std::cerr << "pcap_setfilter: " << pcap_geterr(handle) << std::endl;
        pcap_freecode(&fp);
        pcap_close(handle);
        pcap_freealldevs(alldevs);
        WSACleanup();
        return 1;
    }
    pcap_freecode(&fp);

    // 启动连接表更新线程(每秒更新一次)
    std::thread connUpdater([]() {
        while (true) {
            UpdateConnectionTable();
            std::this_thread::sleep_for(std::chrono::seconds(1));
        }
        });

    // 启动显示线程
    std::thread display(DisplayThread);

    std::cout << "开始捕获数据包,按 Ctrl+C 退出..." << std::endl;

    // 进入捕获循环
    pcap_loop(handle, 0, PacketHandler, nullptr);

    // 清理(正常情况下不会执行到这里,因为pcap_loop是无限循环)
    pcap_close(handle);
    pcap_freealldevs(alldevs);
    connUpdater.join();
    display.join();
    WSACleanup();
    return 0;
}

// 获取本机所有IPv4地址
void GetLocalIPs() {
    PIP_ADAPTER_INFO pAdapterInfo = (IP_ADAPTER_INFO*)malloc(sizeof(IP_ADAPTER_INFO));
    ULONG ulOutBufLen = sizeof(IP_ADAPTER_INFO);
    if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW) {
        free(pAdapterInfo);
        pAdapterInfo = (IP_ADAPTER_INFO*)malloc(ulOutBufLen);
    }
    if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == NO_ERROR) {
        for (PIP_ADAPTER_INFO pAdapter = pAdapterInfo; pAdapter; pAdapter = pAdapter->Next) {
            for (IP_ADDR_STRING* pAddr = &pAdapter->IpAddressList; pAddr; pAddr = pAddr->Next) {
                in_addr addr;
                if (inet_pton(AF_INET, pAddr->IpAddress.String, &addr) == 1) {
                    g_localIPs.push_back(addr);
                }
            }
        }
    }
    free(pAdapterInfo);
}

// 判断IP是否为本机地址
bool IsLocalIP(const in_addr& ip) {
    for (const auto& local : g_localIPs) {
        if (local.s_addr == ip.s_addr) return true;
    }
    return false;
}

// 更新TCP/UDP连接表
void UpdateConnectionTable() {
    std::lock_guard<std::mutex> lock(g_connMutex);
    g_connectionMap.clear();

    // 获取TCP连接表
    PMIB_TCPTABLE_OWNER_PID pTcpTable = nullptr;
    DWORD dwSize = 0;
    DWORD dwRet = GetExtendedTcpTable(nullptr, &dwSize, FALSE, AF_INET, TCP_TABLE_OWNER_PID_ALL, 0);
    if (dwRet == ERROR_INSUFFICIENT_BUFFER) {
        pTcpTable = (PMIB_TCPTABLE_OWNER_PID)malloc(dwSize);
        dwRet = GetExtendedTcpTable(pTcpTable, &dwSize, FALSE, AF_INET, TCP_TABLE_OWNER_PID_ALL, 0);
        if (dwRet == NO_ERROR) {
            for (DWORD i = 0; i < pTcpTable->dwNumEntries; ++i) {
                auto& row = pTcpTable->table[i];
                // 转换端口为主机字节序
                WORD localPort = ntohs((WORD)row.dwLocalPort);
                WORD remotePort = ntohs((WORD)row.dwRemotePort);
                UINT64 key = MakeKey(row.dwLocalAddr, localPort, row.dwRemoteAddr, remotePort, IPPROTO_TCP);
                g_connectionMap[key] = row.dwOwningPid;
            }
        }
        free(pTcpTable);
    }

    // 获取UDP表
    PMIB_UDPTABLE_OWNER_PID pUdpTable = nullptr;
    dwSize = 0;
    dwRet = GetExtendedUdpTable(nullptr, &dwSize, FALSE, AF_INET, UDP_TABLE_OWNER_PID, 0);
    if (dwRet == ERROR_INSUFFICIENT_BUFFER) {
        pUdpTable = (PMIB_UDPTABLE_OWNER_PID)malloc(dwSize);
        dwRet = GetExtendedUdpTable(pUdpTable, &dwSize, FALSE, AF_INET, UDP_TABLE_OWNER_PID, 0);
        if (dwRet == NO_ERROR) {
            for (DWORD i = 0; i < pUdpTable->dwNumEntries; ++i) {
                auto& row = pUdpTable->table[i];
                WORD localPort = ntohs((WORD)row.dwLocalPort);
                // UDP:本地地址+本地端口,远程填0
                UINT64 key = MakeKey(row.dwLocalAddr, localPort, 0, 0, IPPROTO_UDP);
                g_connectionMap[key] = row.dwOwningPid;
            }
        }
        free(pUdpTable);
    }
}

// 根据数据包查找进程ID
DWORD LookupProcess(const IP_HDR* ip, const void* transport, bool isTcp) {
    std::lock_guard<std::mutex> lock(g_connMutex);

    DWORD srcIP = ip->ip_src.s_addr;
    DWORD dstIP = ip->ip_dst.s_addr;
    WORD srcPort = 0, dstPort = 0;
    BYTE proto = isTcp ? IPPROTO_TCP : IPPROTO_UDP;

    if (isTcp) {
        const TCP_HDR* tcp = (const TCP_HDR*)transport;
        srcPort = ntohs(tcp->th_sport);
        dstPort = ntohs(tcp->th_dport);
    }
    else {
        const UDP_HDR* udp = (const UDP_HDR*)transport;
        srcPort = ntohs(udp->uh_sport);
        dstPort = ntohs(udp->uh_dport);
    }

    // 判断数据包方向
    bool upload = IsLocalIP(ip->ip_src);
    UINT64 key;
    if (upload) {
        // 上传:本地=源IP+源端口,远程=目的IP+目的端口
        key = MakeKey(srcIP, srcPort, dstIP, dstPort, proto);
    }
    else {
        // 下载:本地=目的IP+目的端口,远程=源IP+源端口
        key = MakeKey(dstIP, dstPort, srcIP, srcPort, proto);
    }

    auto it = g_connectionMap.find(key);
    if (it != g_connectionMap.end())
        return it->second;

    // 对于UDP,如果未找到,尝试仅用本地地址+端口查找(因为UDP表可能没有远程信息)
    if (!isTcp) {
        UINT64 localKey;
        if (upload) {
            localKey = MakeKey(srcIP, srcPort, 0, 0, proto);
        }
        else {
            localKey = MakeKey(dstIP, dstPort, 0, 0, proto);
        }
        it = g_connectionMap.find(localKey);
        if (it != g_connectionMap.end())
            return it->second;
    }

    return 0; // 未找到,返回0(通常为系统内核或未知)
}
std::string GetExeFileNameByPid(DWORD pid)
{
    // 1. 打开目标进程(需要足够的权限)
    // PROCESS_QUERY_INFORMATION: 查询进程信息的权限
    // PROCESS_VM_READ: 读取进程虚拟内存的权限
    HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
    if (hProcess == NULL)
    {
        // 获取失败时可以打印错误码(可选)
        // DWORD error = GetLastError();
        // printf("OpenProcess failed, error code: %d\n", error);
        return "驱动程序";
    }

    // 2. 定义缓冲区存储EXE路径
    char szExePath[MAX_PATH] = { 0 };
    std::string exeFileName;

    // 3. 获取进程的EXE文件完整路径
    if (GetModuleFileNameExA(hProcess, NULL, szExePath, MAX_PATH))
    {
        // 4. 从完整路径中提取文件名(如从 "C:\Windows\notepad.exe" 提取 "notepad.exe")
        char* pFileName = strrchr(szExePath, '\\');
        if (pFileName != NULL)
        {
            // 跳过反斜杠,取后面的文件名
            exeFileName = pFileName + 1;
        }
        else
        {
            // 特殊情况:路径中无反斜杠,直接使用完整路径
            exeFileName = szExePath;
        }
    }

    // 5. 关闭进程句柄(必须释放资源)
    CloseHandle(hProcess);

    return exeFileName;
}
// 数据包回调函数
void PacketHandler(u_char* user, const struct pcap_pkthdr* pkthdr, const u_char* packet) {
    // 假设以太网帧头(14字节)
    const u_char* ip_data = packet + 14;
    const IP_HDR* ip = (const IP_HDR*)ip_data;

    // IP头长度
    int ipHeaderLen = (ip->ip_verlen & 0x0F) * 4;
    if (ipHeaderLen < 20) return;

    // 只处理TCP和UDP
    bool isTcp = (ip->ip_p == IPPROTO_TCP);
    bool isUdp = (ip->ip_p == IPPROTO_UDP);
    if (!isTcp && !isUdp) return;

    // 数据包总长度(包括IP头)
    unsigned short totalLen = ntohs(ip->ip_len);

    // 确定方向
    bool upload = IsLocalIP(ip->ip_src);
    bool download = IsLocalIP(ip->ip_dst);
    if (!upload && !download) return; // 非本机流量,忽略

    const void* transport = ip_data + ipHeaderLen;

    // 查找进程ID
    DWORD pid = LookupProcess(ip, transport, isTcp);

    // 更新统计
    {
        std::lock_guard<std::mutex> lock(g_statsMutex);
        ProcessStats& stats = g_processStats[pid];
        if (!stats.is_start)
        {
            stats.is_start = true;
            stats.name = GetExeFileNameByPid(pid);
        }
        if (upload) {
            stats.upload += totalLen;
            g_totalUpload.fetch_add(totalLen, std::memory_order_relaxed);
        }
        else {
            stats.download += totalLen;
            g_totalDownload.fetch_add(totalLen, std::memory_order_relaxed);
        }
    }
}


// 显示线程:每秒打印每个进程的累计流量和实时速率
void DisplayThread() {
    using namespace std::chrono;
    auto lastTime = steady_clock::now();
    unsigned long long lastTotalUpload = 0;
    unsigned long long lastTotalDownload = 0;
    std::unordered_map<DWORD, ProcessStats> prevStats;

    while (true) {
        std::this_thread::sleep_for(seconds(1));
        auto now = steady_clock::now();

        // 获取当前总累计
        unsigned long long curTotalUpload = g_totalUpload.load(std::memory_order_relaxed);
        unsigned long long curTotalDownload = g_totalDownload.load(std::memory_order_relaxed);

        // 计算总速率
        unsigned long long totalUpRate = curTotalUpload - lastTotalUpload;
        unsigned long long totalDownRate = curTotalDownload - lastTotalDownload;

        // 获取当前进程统计快照
        std::unordered_map<DWORD, ProcessStats> curStats;
        {
            std::lock_guard<std::mutex> lock(g_statsMutex);
            curStats = g_processStats; // 复制
        }

        // 清屏并输出
        system("cls");
        std::cout << "========== 网络流量统计(按进程) ==========" << std::endl;
        std::cout << "总上传: " << curTotalUpload << " bytes  (速率: " << totalUpRate << " B/s, "
            << totalUpRate / 1024.0 << " KB/s)" << std::endl;
        std::cout << "总下载: " << curTotalDownload << " bytes  (速率: " << totalDownRate << " B/s, "
            << totalDownRate / 1024.0 << " KB/s)" << std::endl;
        std::cout << "--------------------------------------------" << std::endl;
        std::cout << std::left << std::setw(8) << "PID" << std::setw(30)<<"名称"
            << std::setw(15) << "上传速率(B/s)"
            << std::setw(15) << "下载速率(B/s)"
            << std::setw(15) << "累计上传"
            << std::setw(15) << "累计下载" << std::endl;

        for (const auto& entry : curStats) {
            DWORD pid = entry.first;
            const ProcessStats& cur = entry.second;

            // 计算该进程的速率
            unsigned long long upRate = 0, downRate = 0;
            auto it = prevStats.find(pid);
            if (it != prevStats.end()) {
                upRate = cur.upload - it->second.upload;
                downRate = cur.download - it->second.download;
            }

            std::cout << std::setw(8) << pid << std::setw(30)<< cur.name.c_str()
                << std::setw(15) << upRate
                << std::setw(15) << downRate
                << std::setw(15) << cur.upload
                << std::setw(15) << cur.download << std::endl;
        }

        // 更新上一秒数据
        prevStats = std::move(curStats);
        lastTotalUpload = curTotalUpload;
        lastTotalDownload = curTotalDownload;
    }
}
Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐