计算机网络 IP 详解

一、IP 核心基础

1. 什么是 IP 地址?一句话讲透本质

本质是:给接入互联网的每一台设备(电脑、服务器、手机、路由器)分配的一个「唯一网络身份标识」,也可以理解为网络设备的「门牌号」。

所有网络数据的传输(比如你用 C++ 写的客户端给服务器发消息、浏览器访问网页、两台电脑局域网通信),核心逻辑都是:通过「源 IP」找到「目的 IP」,把数据精准送达。没有 IP 地址,网络中所有设备都是孤立的,无法完成任何通信。

2. IPv4 地址

目前主流使用的是 IPv4 协议(IPv6 是趋势,但实战开发中 IPv4 仍是绝对主流),也是本文的核心讲解对象,小白必须吃透。

  • IPv4 地址的底层本质:32 位二进制数 ,比如 11000000.10101000.00000001.00000001
  • 为什么我们看到的是 192.168.1.1?为了人类易读,把 32 位二进制拆成4 个 8 位的二进制段 ,每个段转成「0~255」的十进制数,段之间用 . 分隔,这种格式叫做「点分十进制」。

每个段的取值范围是 0~255,超出这个范围的一定是非法 IP(比如 192.168.1.256),C++ 实战中做 IP 校验的核心规则之一。

3. IPv4 地址的分类

32 位的 IPv4 地址,分为「网络位 」和「主机位 」:网络位标识「哪个网段」,主机位标识「该网段下的哪台设备」。根据网络位的长度,IPv4 分为 A、B、C、D、E 五类,只需要掌握核心的 3 类即可,考点 + 重点如下:

  • A 类地址:1~126.x.x.x ,网络位占 8 位,主机位 24 位,用于大型网络(比如国家骨干网)
  • B 类地址:128~191.x.x.x,网络位占 16 位,主机位 16 位,用于中型企业 / 机构
  • C 类地址:192~223.x.x.x,网络位占 24 位,主机位 8 位,最常用(局域网、家庭路由器、公司内网全是这类)
  • D 类(224~239):组播地址,E 类(240~255):保留地址,只需要知道名称,不用深究。
4. 公有 IP & 私有 IP
  • 私有 IP:局域网内部使用的 IP 地址,不能在公网上通信 ,是所有开发者本地调试代码的标配,3 个固定网段:
    • 10.0.0.0 ~ 10.255.255.255
    • 172.16.0.0 ~ 172.31.255.255
    • 192.168.0.0 ~ 192.168.255.255 最常用,你的路由器、本地电脑的 IP 基本都是这个网段
  • 公有 IP:互联网上的唯一合法 IP,由运营商分配,比如阿里云服务器的 IP、百度官网的 IP,公网设备之间通信必须用公有 IP。

C++ 写本地客户端 / 服务端程序时,绑定的 IP 都是私有 IP(比如 127.0.0.1 本机回环地址、192.168.1.100);部署到云服务器后,绑定的是公有 IP。

5. 网关 & 子网掩码
  • 子网掩码:配合 IP 地址,划分「网络位」和「主机位」 ,比如常见的 255.255.255.0 对应 C 类地址,意思是前 3 段是网络位,最后 1 段是主机位。
  • 网关:局域网的「出口」,比如你的电脑要访问百度,数据先发给网关(路由器 IP,一般是 192.168.1.1),再由网关转发到公网。

二、IP 核心知识点

1. 什么是字节序?

字节序:多字节数据在计算机内存中的存储顺序 ,比如一个 32 位的 IP 整数 0x12345678,占 4 个字节,不同 CPU 的存储方式不同,只有两种:

  • 小端序(Little-Endian):低字节存低地址,高字节存高地址 → 所有 x86/x86_64 架构的 CPU(Intel、AMD)都是小端序,你的电脑、服务器基本都是小端序。
  • 大端序(Big-Endian):高字节存低地址,低字节存高地址 → 也叫「网络字节序」,IP/TCP/UDP 等所有网络协议,统一规定使用大端序传输数据
2. 为什么 C++ 开发者必须掌握?

核心原因:本地主机是小端序,网络传输是大端序。如果你的 C++ 程序直接把本地的 IP 地址、端口号「原封不动」发给网络,对方收到的会是乱码数据,通信直接失败!

比如:你在本地用 C++ 定义端口号 8080(对应十六进制 0x1F90),小端序存储是 0x90 0x1F,如果不转换直接发,对方收到的是 0x901F 对应端口号 36927,完全错误。

3. C++ 中处理字节序的 4 个核心系统函数

所有 C++ 网络编程,只要涉及 IP / 端口的网络传输,都必须用这 4 个函数做「主机序 ↔ 网络序」的转换,函数在 <arpa/inet.h> 头文件中,Linux/Windows 均兼容,记死 + 吃透:

cpp 复制代码
#include <arpa/inet.h>
// 1. 主机32位整数 → 网络32位整数(用于IP地址转换)
uint32_t htonl(uint32_t hostlong); 
// 2. 主机16位整数 → 网络16位整数(用于端口号转换,端口是16位)
uint16_t htons(uint16_t hostshort);
// 3. 网络32位整数 → 主机32位整数(接收IP时转换)
uint32_t ntohl(uint32_t netlong);
// 4. 网络16位整数 → 主机16位整数(接收端口时转换)
uint16_t ntohs(uint16_t netshort);
3. 两种形式的区别与使用场景
  • 字符串形式:192.168.1.1 → 人类易读,用户输入、日志打印都用这个形式。
  • 32 位整数形式:0xC0A80101 → 计算机易处理,C++ 网络编程中所有 IP 的底层运算、传输、存储,都是用 32 位无符号整数,这是核心规则!

所以:C++ 开发中,必须掌握「IP 字符串 ↔ 32 位整数」的双向转换,这是 IP 编程的基础.

4. C++ 中 IP 转换的核心函数

所有函数均在 <arpa/inet.h> 头文件中,Linux/Windows 通用,分为「旧版」和「新版」,全部要会用:

方案 1:旧版函数

cpp 复制代码
#include <arpa/inet.h>
// ① IP字符串 → 32位网络序整数 (核心)
in_addr_t inet_addr(const char *cp);
// ② 32位网络序整数 → IP字符串 (核心)
char *inet_ntoa(struct in_addr in);

inet_addr 返回的是「网络序的 32 位整数」,不用再调用 htonl 转换;inet_ntoa 传入的是in_addr结构体(内部就是 32 位网络序 IP),返回的是点分十进制字符串。

方案 2:新版函数

旧版函数有缺陷(比如inet_ntoa是线程不安全的),C++11 之后推荐使用新版函数

cpp 复制代码
#include <arpa/inet.h>
// ① 任意IP格式 → 网络序整数(IPv4填AF_INET,IPv6填AF_INET6)
int inet_pton(int af, const char *src, void *dst);
// ② 网络序整数 → 任意IP格式字符串
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);

旧版仅支持 IPv4,有线程安全问题;新版兼容 IPv4/IPv6,线程安全,推荐使用。

5. sockaddr_in 结构体

核心地位

在 C++ 的 socket 网络编程中,所有的「IP 地址」+「端口号」的组合,都必须封装到 sockaddr_in 结构体中,没有例外!不管是绑定本地 IP + 端口、连接远程服务器 IP + 端口、接收客户端 IP + 端口,全靠这个结构体。

结构体定义 + 核心成员解析

cpp 复制代码
#include <netinet/in.h>
// IPv4专用的地址结构体
struct sockaddr_in {
    sa_family_t     sin_family;   // 地址族,固定填 AF_INET (标识IPv4)
    uint16_t        sin_port;     // 端口号,必须是 网络序 (要调用htons转换)
    struct in_addr  sin_addr;     // IP地址,内部是32位网络序整数(in_addr_t)
    char            sin_zero[8];  // 填充位,无意义,填0即可
};
// 嵌套的IP地址结构体
struct in_addr {
    in_addr_t       s_addr;       // 核心:32位网络序的IPv4地址整数
};

结构体赋值的标准写法

赋值顺序:填地址族 → 转端口为网络序 → 转 IP 为网络序:

cpp 复制代码
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr)); // 初始化结构体,所有成员置0
addr.sin_family = AF_INET;      // 固定:IPv4
addr.sin_port = htons(8080);    // 端口号:主机序 → 网络序
// 方式1:直接赋值IP(字符串转网络序整数)
addr.sin_addr.s_addr = inet_addr("192.168.1.1");
// 方式2:新版函数赋值IP(推荐)
inet_pton(AF_INET, "192.168.1.1", &addr.sin_addr);

三、IP 相关核心代码

1. IP 字符串 ↔ 32 位整数 互转
cpp 复制代码
#include <iostream>
#include <arpa/inet.h>
#include <cstring>
using namespace std;

// IP字符串 -> 32位主机序整数
uint32_t ip_str_to_uint(const char* ip) {
    uint32_t net_ip = inet_addr(ip); // 得到网络序整数
    return ntohl(net_ip);            // 转为主机序,方便打印/运算
}

// 32位主机序整数 -> IP字符串
string ip_uint_to_str(uint32_t ip_uint) {
    uint32_t net_ip = htonl(ip_uint); // 转为网络序
    struct in_addr addr;
    addr.s_addr = net_ip;
    return string(inet_ntoa(addr));   // 转为字符串
}

int main() {
    const char* ip = "192.168.1.100";
    // 转换测试
    uint32_t ip_uint = ip_str_to_uint(ip);
    cout << "IP字符串: " << ip << " → 32位整数: " << ip_uint << endl;
    cout << "32位整数: " << ip_uint << " → IP字符串: " << ip_uint_to_str(ip_uint) << endl;
    return 0;
}

编译运行:g++ -o ip_convert ip_convert.cpp && ./ip_convert

运行结果:192.168.1.100 → 3232235876,反向转换一致,验证正确。

2. 校验 IP 地址是否合法

开发中用户输入的 IP 可能是非法的(比如 192.168.1.256、256.0.0.1、192.168.1),必须做合法性校验

cpp 复制代码
#include <iostream>
#include <arpa/inet.h>
#include <cstring>
using namespace std;

// 方案1:调用系统函数(推荐,简洁高效,实战首选)
bool is_ip_valid(const char* ip) {
    struct sockaddr_in addr;
    return inet_pton(AF_INET, ip, &addr.sin_addr) == 1;
}

// 方案2:手动实现(面试常考,考察基础功底,纯C++逻辑)
bool is_ip_valid_manual(const char* ip) {
    int count = 0; // 统计点的个数,必须是3个
    int num = 0;   // 统计每个段的数值
    for (int i = 0; ip[i] != '\0'; i++) {
        if (ip[i] == '.') {
            count++;
            if (count > 3 || num <0 || num >255) return false;
            num = 0;
        } else if (isdigit(ip[i])) {
            num = num *10 + (ip[i]-'0');
            if (num >255) return false; // 超过255直接非法
        } else {
            return false; // 包含非数字字符
        }
    }
    // 最后一个段校验 + 点的个数必须是3
    return count ==3 && num >=0 && num <=255;
}

int main() {
    const char* ip1 = "192.168.1.1";
    const char* ip2 = "192.168.1.256";
    cout << ip1 << " 合法吗?" << (is_ip_valid(ip1) ? "是" : "否") << endl;
    cout << ip2 << " 合法吗?" << (is_ip_valid(ip2) ? "是" : "否") << endl;
    return 0;
}
3. Socket 绑定 IP + 端口
cpp 复制代码
#include <iostream>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <cstring>
#include <unistd.h>
using namespace std;

int main() {
    // 1. 创建TCP socket
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd < 0) { perror("socket error"); return -1; }

    // 2. 初始化sockaddr_in结构体,绑定IP+端口
    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;                // IPv4地址族
    server_addr.sin_port = htons(8080);              // 端口:主机序→网络序
    server_addr.sin_addr.s_addr = inet_addr("0.0.0.0"); // 绑定本机所有IP(0.0.0.0的特殊含义)

    // 3. 绑定操作
    if (bind(fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) <0) {
        perror("bind error");
        close(fd);
        return -1;
    }

    cout << "成功绑定IP: 0.0.0.0, 端口: 8080" << endl;
    close(fd);
    return 0;
}

0.0.0.0 的含义 ------ 绑定本机所有的网卡 IP,客户端可以通过本机的任意 IP(127.0.0.1、192.168.1.100)访问该服务,这是开发中的标配写法。

相关推荐
Howrun7772 小时前
计算机网络_UDP和TCP
tcp/ip·计算机网络·udp
翼龙云_cloud2 小时前
阿里云渠道商:云服务计费 按量 包年 预留实例怎么选?
服务器·阿里云·云计算
Code Warrior2 小时前
【Linux】多路转接poll、epoll
linux·服务器
Web极客码2 小时前
WordPress维护指南
服务器·网络·wordpress
技术性摸鱼2 小时前
计算机网络-知识点考点
计算机网络·系统架构
DARLING Zero two♡3 小时前
【计算机网络】简学深悟启示录:序列化&&反序列化
开发语言·计算机网络·php
西***63473 小时前
全场景音视频赋能:三大综合管理平台技术与落地实践
服务器
阿拉伯柠檬3 小时前
网络层与网络层协议IP(一)
linux·网络·网络协议·tcp/ip·面试
gaize12133 小时前
阿里云ECS云服务器怎么样?值得入手吗?最新测评
服务器·阿里云·云计算