You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Windows 10中基于IP Helper API统计特定UDP连接收发字节数的方法

Great question! You’re spot-on that IP Helper API mirrors most TCP functionality with UDP equivalents (like GetTcpTable()/GetUdpTable()), but there’s no public GetPerUdpConnectionEStats() function—and that’s not an oversight, it’s tied to how UDP works at the network level.


Why No GetPerUdpConnectionEStats()?

UDP is a connectionless protocol, meaning the Windows TCP/IP stack doesn’t maintain persistent "connection" contexts the way it does for TCP (which tracks state like SYN, ESTABLISHED, FIN). Since UDP has no formal connection lifecycle, there’s no built-in system-level structure to store per-connection byte stats—hence no dedicated API to retrieve them directly.


Workable Alternatives to Track UDP Byte Counts

You’ll need to implement custom tracking using Windows’ built-in tracing or filtering frameworks. Here are the most reliable approaches:

1. Event Tracing for Windows (ETW) – Non-Intrusive Stats

ETW lets you capture UDP send/receive events directly from the TCP/IP stack, then aggregate byte counts by logical "connection" (defined by the 5-tuple: source IP, source port, destination IP, destination port, protocol).

Key Steps:

  • Enable the Microsoft-Windows-TCPIP ETW provider
  • Listen for two critical events:
    • UdpSend (Event ID 1000): Captures outgoing UDP packet details, including byte count and 5-tuple
    • UdpReceive (Event ID 1001): Captures incoming UDP packet details
  • Maintain a dictionary (keyed by the 5-tuple) to accumulate send/receive bytes over time

Simplified C++ Example Snippet:

#include <windows.h>
#include <evntrace.h>
#include <unordered_map>

// Define a struct to represent the UDP 5-tuple
struct UdpConnectionKey {
    UINT32 srcIp;
    UINT16 srcPort;
    UINT32 dstIp;
    UINT16 dstPort;

    // Hash function for use as a dictionary key
    bool operator==(const UdpConnectionKey& other) const {
        return srcIp == other.srcIp && srcPort == other.srcPort &&
               dstIp == other.dstIp && dstPort == other.dstPort;
    }
};

namespace std {
    template<> struct hash<UdpConnectionKey> {
        size_t operator()(const UdpConnectionKey& k) const {
            return hash<UINT32>()(k.srcIp) ^ hash<UINT16>()(k.srcPort) ^
                   hash<UINT32>()(k.dstIp) ^ hash<UINT16>()(k.dstPort);
        }
    };
}

std::unordered_map<UdpConnectionKey, ULONG64> sendStats;
std::unordered_map<UdpConnectionKey, ULONG64> recvStats;

void WINAPI EventCallback(PEVENT_RECORD pEventRecord) {
    if (pEventRecord->EventHeader.EventDescriptor.Id == 1000) {
        // Parse UdpSend event data (schema documented by Microsoft)
        UdpConnectionKey key = {/* extract 5-tuple from event data */};
        ULONG bytesSent = {/* extract byte count from event data */};
        sendStats[key] += bytesSent;
    } else if (pEventRecord->EventHeader.EventDescriptor.Id == 1001) {
        // Parse UdpReceive event data
        UdpConnectionKey key = {/* extract 5-tuple from event data */};
        ULONG bytesReceived = {/* extract byte count from event data */};
        recvStats[key] += bytesReceived;
    }
}

int main() {
    EVENT_TRACE_PROPERTIES* traceProps = (EVENT_TRACE_PROPERTIES*)malloc(sizeof(EVENT_TRACE_PROPERTIES) + 1024);
    ZeroMemory(traceProps, sizeof(EVENT_TRACE_PROPERTIES) + 1024);
    traceProps->Wnode.BufferSize = sizeof(EVENT_TRACE_PROPERTIES) + 1024;
    traceProps->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES);
    wcscpy_s((WCHAR*)((BYTE*)traceProps + traceProps->LoggerNameOffset), 512, L"UdpStatsSession");

    TRACEHANDLE sessionHandle;
    StartTrace(&sessionHandle, L"UdpStatsSession", traceProps);
    EnableTraceEx2(sessionHandle, &GUID_MICROSOFT_WINDOWS_TCPIP, EVENT_CONTROL_CODE_ENABLE_PROVIDER, TRACE_LEVEL_INFORMATION, 0, 0, 0, NULL);
    
    // Process events until stopped (e.g., user input)
    ProcessTrace(&sessionHandle, 1, NULL, NULL);

    // Cleanup
    DisableTraceEx2(sessionHandle, &GUID_MICROSOFT_WINDOWS_TCPIP, EVENT_CONTROL_CODE_DISABLE_PROVIDER, 0);
    StopTrace(sessionHandle, L"UdpStatsSession", traceProps);
    free(traceProps);

    return 0;
}

2. Windows Filtering Platform (WFP) – Intrusive but Flexible

WFP lets you intercept UDP packets at multiple layers of the network stack, making it ideal if you need real-time tracking or want to filter traffic alongside stats collection.

Key Steps:

  • Initialize a WFP filtering engine
  • Register a filter rule targeting UDP traffic (e.g., at the FWPM_LAYER_INBOUND_TRANSPORT_V4/OUTBOUND_TRANSPORT_V4 layers)
  • In the filter callback, extract the 5-tuple and packet byte count, then update your stats

Simplified C++ Example Snippet:

#include <windows.h>
#include <fwpsk.h>
#include <fwpmu.h>

std::unordered_map<UdpConnectionKey, ULONG64> sendStats;
std::unordered_map<UdpConnectionKey, ULONG64> recvStats;

NTSTATUS CALLBACK UdpStatsClassifyFn(
    IN const FWPS_INCOMING_VALUES* inFixedValues,
    IN const FWPS_INCOMING_METADATA_VALUES* inMetaValues,
    IN OUT VOID* layerData,
    IN const FWPS_FILTER* filter,
    IN UINT64 flowContext,
    OUT FWPS_CLASSIFY_OUT* classifyOut
) {
    UNREFERENCED_PARAMETER(filter);
    UNREFERENCED_PARAMETER(flowContext);

    // Extract packet byte count
    UINT32 byteCount = 0;
    if (inMetaValues->layerId == FWPM_LAYER_OUTBOUND_TRANSPORT_V4) {
        FWPS_TRANSPORT_SEND_PARAMS* sendParams = (FWPS_TRANSPORT_SEND_PARAMS*)layerData;
        byteCount = sendParams->dataLength;
        // Extract 5-tuple from inFixedValues
        UdpConnectionKey key = {
            inFixedValues->incomingValue[FWPS_FIELD_OUTBOUND_TRANSPORT_V4_IP_LOCAL_ADDRESS].value.uint32,
            inFixedValues->incomingValue[FWPS_FIELD_OUTBOUND_TRANSPORT_V4_IP_LOCAL_PORT].value.uint16,
            inFixedValues->incomingValue[FWPS_FIELD_OUTBOUND_TRANSPORT_V4_IP_REMOTE_ADDRESS].value.uint32,
            inFixedValues->incomingValue[FWPS_FIELD_OUTBOUND_TRANSPORT_V4_IP_REMOTE_PORT].value.uint16
        };
        sendStats[key] += byteCount;
    } else if (inMetaValues->layerId == FWPM_LAYER_INBOUND_TRANSPORT_V4) {
        FWPS_TRANSPORT_RECEIVE_PARAMS* recvParams = (FWPS_TRANSPORT_RECEIVE_PARAMS*)layerData;
        byteCount = recvParams->dataLength;
        // Extract 5-tuple from inFixedValues
        UdpConnectionKey key = {
            inFixedValues->incomingValue[FWPS_FIELD_INBOUND_TRANSPORT_V4_IP_REMOTE_ADDRESS].value.uint32,
            inFixedValues->incomingValue[FWPS_FIELD_INBOUND_TRANSPORT_V4_IP_REMOTE_PORT].value.uint16,
            inFixedValues->incomingValue[FWPS_FIELD_INBOUND_TRANSPORT_V4_IP_LOCAL_ADDRESS].value.uint32,
            inFixedValues->incomingValue[FWPS_FIELD_INBOUND_TRANSPORT_V4_IP_LOCAL_PORT].value.uint16
        };
        recvStats[key] += byteCount;
    }

    // Allow the packet to proceed
    classifyOut->actionType = FWP_ACTION_PERMIT;
    return STATUS_SUCCESS;
}

// Helper function to register the filter
HRESULT RegisterUdpStatsFilter() {
    FWPM_SESSION session = {0};
    session.flags = FWPM_SESSION_FLAG_DYNAMIC;
    HANDLE engineHandle;
    HRESULT hr = FwpmEngineOpen(NULL, RPC_C_AUTHN_WINNT, NULL, &session, &engineHandle);
    if (FAILED(hr)) return hr;

    // Register callout
    FWPM_CALLOUT callout = {0};
    callout.calloutKey = {/* unique GUID for your callout */};
    callout.displayData.name = L"UDP Stats Callout";
    callout.classifyFn = UdpStatsClassifyFn;
    hr = FwpmCalloutAdd(engineHandle, &callout, NULL, NULL);
    if (FAILED(hr)) {
        FwpmEngineClose(engineHandle);
        return hr;
    }

    // Create filter rule targeting UDP
    FWPM_FILTER filter = {0};
    filter.layerKey = FWPM_LAYER_OUTBOUND_TRANSPORT_V4;
    filter.action.type = FWP_ACTION_CALLOUT_TERMINATING;
    filter.action.calloutKey = callout.calloutKey;
    filter.filterCondition[0].fieldKey = FWPS_FIELD_OUTBOUND_TRANSPORT_V4_IP_PROTOCOL;
    filter.filterCondition[0].matchType = FWP_MATCH_EQUAL;
    filter.filterCondition[0].conditionValue.type = FWP_UINT8;
    filter.filterCondition[0].conditionValue.uint8 = IPPROTO_UDP;
    hr = FwpmFilterAdd(engineHandle, &filter, NULL, NULL);

    // Repeat filter setup for inbound layer if needed

    FwpmEngineClose(engineHandle);
    return hr;
}

Final Notes
  • ETW is the best choice for non-intrusive, low-overhead stats collection.
  • WFP is better if you need to interact with traffic (e.g., block packets) alongside tracking.
  • Both approaches require admin privileges to run, as they access low-level network stack data.

内容的提问来源于stack exchange,提问作者u17

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 07:28:32