首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

C++ 實戰:本機網卡 IP 數據包捕獲與解析完整指南

     編輯:關於C++

項目需求

編寫 C++ 程序捕獲本機網卡上的 IP 數據包,並解析輸出關鍵字段:版本號、總長度、標志位、片偏移、協議類型、源地址、目的地址。

IP 數據報頭部結構詳解

IPv4 數據報頭部固定 20 字節,包含以下關鍵字段(位域結構):

字段位數說明
版本4IPv4 為 4,IPv6 為 6
首部長度4單位 4 字節,最小 5 (20 字節),最大 15 (60 字節)
服務類型8含優先權、TOS 子字段,現多忽略
總長度16首部+數據總字節數,最大 65535
標識16唯一標識數據報,分片共享同一標識
標志位3DF(不分片)、MF(更多片段)
片偏移13分片在原數據報中的偏移 (單位 8 字節)
生存時間 TTL8最大路由躍點數,防止循環
協議類型8上層協議:1=ICMP, 6=TCP, 17=UDP 等
首部校驗和16僅校驗首部,網際校驗和算法
源/目的地址32+32IPv4 地址

關鍵技術點

  1. 原始套接字:socket(AF_INET, SOCK_RAW, IPPROTO_IP) 接收 IP 層數據。
  2. 混亂模式:WSAIoctl(sock, SIO_RCVALL, ...) 開啟接收所有經過綁定網卡的數據包。
  3. 網絡字節序轉換:ntohs(), ntohl(), inet_ntoa() 處理大端序。
  4. 結構體對齊:使用 #pragma pack(1) 或位域確保與協議頭部一致。
  5. 管理員權限:原始套接字需要提權運行。

核心代碼實現 (Windows 平台)

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <winsock2.h>
#include <windows.h>
#include <mstcpip.h>
#pragma comment(lib, "Ws2_32.lib")
using namespace std;

// IP 首部結構體 (對應 IPv4 固定 20 字節)
typedef struct _IP_HEAD {
    BYTE  ver_hlen;       // 版本(高4位) + 首部長度(低4位)
    BYTE  tos;            // 服務類型
    WORD  total_len;      // 總長度 (網絡序)
    WORD  id;             // 標識 (網絡序)
    WORD  flags_frag;     // 標志位(高3位) + 片偏移(低13位)
    BYTE  ttl;            // 生存時間
    BYTE  protocol;       // 協議類型
    WORD  checksum;       // 首部校驗和 (網絡序)
    DWORD src_addr;       // 源地址 (網絡序)
    DWORD dst_addr;       // 目的地址 (網絡序)
} IP_HEAD;

int packet_count = 0;

void DecodeIP(char* buffer, int len) {
    if (len < sizeof(IP_HEAD)) return;
    
    IP_HEAD* ip = (IP_HEAD*)buffer;
    cout << "第 " << ++packet_count << " 個 IP 數據包信息:" << endl;
    cout << "  版本號: " << (ip->ver_hlen >> 4) << endl;
    cout << "  首部長度: " << ((ip->ver_hlen & 0x0F) * 4) << " 字節" << endl;
    cout << "  總長度: " << ntohs(ip->total_len) << " 字節" << endl;
    cout << "  標識: 0x" << hex << ntohs(ip->id) << dec << endl;
    cout << "  標志位: DF=" << ((ip->flags_frag >> 14) & 1) 
         << " MF=" << ((ip->flags_frag >> 13) & 1) << endl;
    cout << "  片偏移: " << (ip->flags_frag & 0x1FFF) * 8 << " 字節" << endl;
    cout << "  TTL: " << (int)ip->ttl << endl;
    cout << "  協議: " << (int)ip->protocol 
         << (ip->protocol==6?" (TCP)":ip->protocol==17?" (UDP)":ip->protocol==1?" (ICMP)":"") << endl;
    cout << "  源地址: " << inet_ntoa(*(in_addr*)&ip->src_addr) << endl;
    cout << "  目的地址: " << inet_ntoa(*(in_addr*)&ip->dst_addr) << endl;
    cout << "------------------------------------------------------------" << endl;
}

int main() {
    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2,2), &wsaData)) {
        cerr << "WSAStartup 失敗: " << WSAGetLastError() << endl;
        return -1;
    }
    atexit(WSACleanup);

    // 創建原始套接字
    SOCKET sock = socket(AF_INET, SOCK_RAW, IPPROTO_IP);
    if (sock == INVALID_SOCKET) {
        cerr << "Socket 創建失敗: " << WSAGetLastError() << endl;
        return -1;
    }

    // 獲取本機 IP 並綁定
    char hostname[128];
    gethostname(hostname, sizeof(hostname));
    hostent* host = gethostbyname(hostname);
    sockaddr_in addr = {AF_INET, 0, *(in_addr*)host->h_addr_list[0]};
    if (bind(sock, (sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) {
        cerr << "綁定失敗: " << WSAGetLastError() << endl;
        closesocket(sock);
        return -1;
    }

    // 開啟混亂模式
    DWORD dwValue = 1, dwBytesReturned = 0;
    if (WSAIoctl(sock, SIO_RCVALL, &dwValue, sizeof(dwValue), 
                 NULL, 0, &dwBytesReturned, NULL, NULL) == SOCKET_ERROR) {
        cerr << "SIO_RCVALL 失敗: " << WSAGetLastError() << endl;
        closesocket(sock);
        return -1;
    }

    // 設置阻塞模式
    u_long mode = 0;
    ioctlsocket(sock, FIONBIO, &mode);

    cout << "開始捕獲數據包... (Ctrl+C 停止)" << endl;
    char buffer[65535];
    int len;
    while ((len = recv(sock, buffer, sizeof(buffer), 0)) > 0) {
        DecodeIP(buffer, len);
    }

    closesocket(sock);
    return 0;
}

運行說明

  1. 使用 Visual Studio 編譯 (需鏈接 Ws2_32.lib)。
  2. 生成的 .exe 必須以管理員身份運行,否則 SIO_RCVALL 會失敗 (錯誤碼 10013/10022)。
  3. 程序將持續輸出捕獲到的 IP 包解析信息,按 Ctrl+C 停止。

常見問題排查

  • 無輸出:確認管理員權限、防火牆設置、網卡是否有流量。
  • 解析錯誤:檢查結構體對齊,確認 sizeof(IP_HEAD) == 20。
  • 只捕獲發送包:某些系統需設置 SIO_RCVALL_IGMPMCAST 或使用 NDIS/Libpcap/WinPcap 替代方案。

此實現展示了 Windows 平台下底層網絡編程的核心流程,是理解網絡協議、開發嗅探器/防火牆的基礎。生產環境建議使用成熟庫如 WinPcap/Npcap 或 Linux 上的 libpcap。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved