C++ 检测 IPv4 和 IPv6 地址合法性

C++ 检测 IPv4 和 IPv6 地址合法性

概述

在网络编程中,经常需要验证 IP 地址的合法性。本文介绍两种在 C++ 中检测 IPv4 和 IPv6 地址合法性的方法:使用系统原生接口和手动解析验证。系统接口方法简单可靠,推荐优先使用;手动解析方法适用于无法使用系统接口的特殊场景。

方法一:使用系统原生接口(推荐)

利用 POSIX 标准中的 inet_pton 函数,该函数可同时支持 IPv4 和 IPv6 地址的验证,能正确处理各种合法格式(包括 IPv6 的压缩格式)。

#include <iostream>
#include <string>
#include <arpa/inet.h>  // 包含inet_pton函数

// 检测IPv4地址合法性
bool is_valid_ipv4(const std::string& ip) {
    struct in_addr addr;
    // inet_pton返回1表示成功,0表示格式错误,-1表示地址族错误
    return inet_pton(AF_INET, ip.c_str(), &addr) == 1;
}

// 检测IPv6地址合法性
bool is_valid_ipv6(const std::string& ip) {
    struct in6_addr addr;
    return inet_pton(AF_INET6, ip.c_str(), &addr) == 1;
}

// 检测是否为合法IP(IPv4或IPv6)
bool is_valid_ip(const std::string& ip) {
    return is_valid_ipv4(ip) || is_valid_ipv6(ip);
}

int main() {
    // 测试用例
    std::string ipv4_test1 = "192.168.1.1";
    std::string ipv4_test2 = "256.0.0.1";  // 非法(256超过255)
    std::string ipv6_test1 = "2001:0db8:85a3:0000:0000:8a2e:0370:7334";
    std::string ipv6_test2 = "2001:db8:85a3::8a2e:370:7334";  // 压缩格式
    std::string ipv6_test3 = "2001:db8:85a3:::8a2e:370:7334"; // 非法(双冒号过多)

    std::cout << ipv4_test1 << " 是" << (is_valid_ipv4(ipv4_test1) ? "" : "非") << "合法IPv4\n";
    std::cout << ipv4_test2 << " 是" << (is_valid_ipv4(ipv4_test2) ? "" : "非") << "合法IPv4\n";
    std::cout << ipv6_test1 << " 是" << (is_valid_ipv6(ipv6_test1) ? "" : "非") << "合法IPv6\n";
    std::cout << ipv6_test2 << " 是" << (is_valid_ipv6(ipv6_test2) ? "" : "非") << "合法IPv6\n";
    std::cout << ipv6_test3 << " 是" << (is_valid_ipv6(ipv6_test3) ? "" : "非") << "合法IPv6\n";

    return 0;
}

代码解析

  1. 核心函数

    • inet_pton:将点分十进制(IPv4)或冒号分隔(IPv6)的 IP 地址字符串转换为二进制格式
    • 函数返回值:1 表示转换成功(地址合法),0 表示格式错误,-1 表示地址族错误
  2. 函数说明

    • is_valid_ipv4:检测 IPv4 地址,使用 AF_INET 地址族
    • is_valid_ipv6:检测 IPv6 地址,使用 AF_INET6 地址族
    • is_valid_ip:检测是否为合法 IP 地址(IPv4 或 IPv6)
  3. 测试用例

    • 包含合法和非法的 IPv4 地址
    • 包含完整格式、压缩格式和非法格式的 IPv6 地址

编译与运行

  • Linux/macOS

    g++ ip_checker.cpp -o ip_checker && ./ip_checker
    
  • Windows(需要链接 Winsock 库):

    g++ ip_checker.cpp -o ip_checker -lws2_32 && ip_checker.exe
    

方法二:手动解析验证

当无法使用系统接口时,可以通过手动解析字符串来验证 IP 地址格式。这种方法需要自己处理各种格式规则,实现相对复杂。

IPv4 手动验证

#include <string>
#include <sstream>
#include <vector>
#include <cctype>

// 分割字符串
std::vector<std::string> split(const std::string& s, char delim) {
    std::vector<std::string> parts;
    std::string part;
    std::istringstream iss(s);
    while (std::getline(iss, part, delim)) {
        parts.push_back(part);
    }
    return parts;
}

// 手动检测IPv4(点分十进制)
bool is_valid_ipv4_manual(const std::string& ip) {
    auto parts = split(ip, '.');
    
    // 必须有4个部分
    if (parts.size() != 4) return false;

    for (const auto& part : parts) {
        // 每个部分不能为空且长度不能超过3
        if (part.empty() || part.size() > 3) return false;
        
        // 检查是否全为数字
        for (char c : part) {
            if (!std::isdigit(c)) return false;
        }
        
        // 检查范围是否在0-255之间
        int num = std::stoi(part);
        if (num < 0 || num > 255) return false;
        
        // 检查是否有前导零(如"01"是非法的,"0"是合法的)
        if (part.size() > 1 && part[0] == '0') return false;
    }
    
    return true;
}

IPv6 手动验证

// 手动检测IPv6(冒号分隔)
bool is_valid_ipv6_manual(const std::string& ip) {
    auto parts = split(ip, ':');
    
    // IPv6地址段数应在2-8之间
    if (parts.size() < 2 || parts.size() > 8) return false;
    
    // 检查压缩符"::"是否出现且仅出现一次
    int double_colon_count = 0;
    for (const auto& part : parts) {
        if (part.empty()) {
            double_colon_count++;
        }
    }
    if (double_colon_count > 1) return false;
    
    // 检查每个段是否为合法的十六进制值
    for (const auto& part : parts) {
        if (part.empty()) continue;  // 跳过压缩段
        
        // 每个段长度不能超过4
        if (part.size() > 4) return false;
        
        // 检查是否为十六进制字符
        for (char c : part) {
            if (!std::isxdigit(c)) return false;
        }
    }
    
    return true;
}

手动解析注意事项

  1. IPv4 验证要点

    • 必须由 4 个部分组成,用点号分隔
    • 每个部分为 0-255 之间的整数
    • 不能有前导零(除非该部分就是 0)
  2. IPv6 验证要点

    • 必须由 2-8 个部分组成,用冒号分隔
    • 每个部分为 0-FFFF 之间的十六进制数
    • 支持压缩格式(::),但只能出现一次
    • 不处理特殊地址(如本地回环地址 ::1)的特殊验证
  3. 局限性

    • 手动解析容易遗漏边缘情况
    • 不支持所有合法格式(如 IPv6 的内嵌 IPv4 地址)
    • 实现复杂,维护成本高

总结

方法优点缺点适用场景
系统接口(inet_pton简单可靠、支持所有合法格式、维护成本低依赖系统接口大多数常规场景
手动解析不依赖系统接口实现复杂、易出错、不支持所有格式无法使用系统接口的特殊场景

推荐优先使用系统接口方法,它能正确处理各种复杂的 IP 地址格式,且实现简单可靠。在必须使用手动解析的场景下,应进行充分的测试,覆盖各种边缘情况。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bkspiderx

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值