Linux 之 【网络套接字编程】(网络字节序、字节序转换函数、套接字编程类型、标准套接字编程的头文件、sockaddr结构、整数IP与字符串IP的转换)

目录

一、网络字节序

定义

字节序转换函数

概览

命名规律

htons

htonl

ntohl

ntohs

二、套接字编程的类型

域间套接字:同一机器内的进程间通信

原始套接字:直接操作网络层及以下的数据包

网络套接字:跨网络的用户间通信

三、标准套接字编程的头文件

四、sockaddr结构

核心设计思想:通用接口与具体实现

[通用地址结构:struct sockaddr](#通用地址结构:struct sockaddr)

[IPv4 专用地址结构:struct sockaddr_in](#IPv4 专用地址结构:struct sockaddr_in)

bzero

[IPv4 地址封装:struct in_addr](#IPv4 地址封装:struct in_addr)

整数IP<--->字符串IP

inet_addr

inet_pton

inet_ntoa

inet_ntop

结构体关系与强制转换示例


一、网络字节序

定义

TCP/IP协议强制规定,网络上传输的数据流必须使用大端字节序(低地址存放高位字节)

通俗理解:像我们写数字一样,左边(低地址)是高位,右边(高地址)是低位。例如 0x12345678,先发 0x12,最后发 0x78。

  • 统一原因

互联网由不同架构的机器组成(x86是小端,PowerPC/某些网络设备是大端)

网络传输严格遵循从低地址到高地址的顺序发送和接收 :发送端将数据在内存中从低地址字节开始依次发出,接收端按收到顺序依次存入本地的低地址到高地址。这意味着网络上的字节流顺序完全取决于发送端的内存布局------小端机发送时先发低位字节,大端机先发高位字节 。接收端则原样按序存入内存,最终解释出的数值由本机字节序决定 。这就是为什么直接发送会导致跨平台错误,而使用htons/ntohs统一转换为网络字节序(大端)发送,能保证无论发送端是什么机器,网络上的字节流都是"高位在前"的标准顺序,接收端再根据自身字节序正确还原

机器类型 内存表示 (低→高) 发送顺序 网络字节流 大端机接收解释 小端机接收解释
小端机发 78 56 34 12 78→56→34→12 78 56 34 12 0x78563412 0x12345678
大端机发 12 34 56 78 12→34→56→78 12 34 56 78 0x12345678 0x78563412
  • 发送端(编码)

通过将数据从主机字节序转换为网络字节序,保证线上的数据流符合 TCP/IP 标准

如果是大端机:不需要转(或者说转了也不变)

如果是小端机:必须翻转字节顺序

  • 接收端(解码)

通过将数据从网络字节序转换回主机字节序,保证本地CPU能够读取到正确的数据

如果是大端机:不需要转

如果是小端机:必须翻转字节顺序

字节序转换函数

概览

函数名 全称 功能 应用场景
htonl Host to Network Long 主机序 → 网络序 (32位) 发送IP地址前转换
htons Host to Network Short 主机序 → 网络序 (16位) 发送端口号前转换
ntohl Network to Host Long 网络序 → 主机序 (32位) 收到IP地址后转换
ntohs Network to Host Short 网络序 → 主机序 (16位) 收到端口号后转换

如果主机是大端:函数直接返回原值(空操作,效率高)

如果主机是小端:函数进行字节翻转(Bit Flip)后返回

命名规律

h = Host(主机字节序)

n = Network(网络字节序)

l = Long(32位整数,如IP地址)

s = Short(16位整数,如端口号)

to = 转换方向

htons

项目 说明
函数名 htons
全称 Host to Network Short
原型 uint16_t htons(uint16_t hostshort);
参数 hostshort:待转换的16位主机字节序整数
返回值 转换后的16位网络字节序整数
头文件 <arpa/inet.h>
功能 将16位整数从主机字节序转换为网络字节序
应用场景 发送端口号前转换

htonl

项目 说明
函数名 htonl
全称 Host to Network Long
原型 uint32_t htonl(uint32_t hostlong);
参数 hostlong:待转换的32位主机字节序整数
返回值 转换后的32位网络字节序整数
头文件 <arpa/inet.h>
功能 将32位整数从主机字节序转换为网络字节序
应用场景 发送IP地址前转换

ntohl

项目 说明
函数名 ntohl
全称 Network to Host Long
原型 uint32_t ntohl(uint32_t netlong);
参数 netlong:待转换的32位网络字节序整数
返回值 转换后的32位主机字节序整数
头文件 <arpa/inet.h>
功能 将32位整数从网络字节序转换回主机字节序
应用场景 接收到IP地址后解析

ntohs

项目 说明
函数名 ntohs
全称 Network to Host Short
原型 uint16_t ntohs(uint16_t netshort);
参数 netshort:待转换的16位网络字节序整数
返回值 转换后的16位主机字节序整数
头文件 <arpa/inet.h>
功能 将16位整数从网络字节序转换回主机字节序
应用场景 接收到端口号后解析

二、套接字编程的类型

域间套接字:同一机器内的进程间通信

项目 说明
名称 Unix Domain Socket (UDS) / 本地套接字
通信范围 同一台主机内部
地址形式 文件路径(如 /tmp/test.sock
核心优势 高效:不经过网络协议栈,不进行报文封装和校验,直接在内核层面拷贝数据,比TCP本地环回(127.0.0.1)更快
典型场景 Nginx与FastCGI通信、MySQL本地连接、Docker守护进程与客户端通信
编程特点 用法与网络套接字类似,但地址族使用 AF_UNIXAF_LOCAL

原始套接字:直接操作网络层及以下的数据包

项目 说明
名称 Raw Socket
通信范围 跨网络(但工作在更底层)
核心能力 允许程序员自己构造IP头部、ICMP头部等,绕过TCP/UDP协议栈
典型场景 Ping工具(构造ICMP包)、Traceroute、Wireshark等抓包工具、网络攻击与防御工具
编程特点 需要root权限,地址族使用 AF_PACKET(Linux)或 AF_INET 配合 SOCK_RAW,需要自己处理协议细节
注意事项 不经过传输层,所以没有TCP的可靠性和端口概念

网络套接字:跨网络的用户间通信

项目 说明
名称 Network Socket / Internet Socket
通信范围 不同主机之间(局域网或互联网)
地址形式 IP地址 + 端口号
核心协议 TCP (SOCK_STREAM) 和 UDP (SOCK_DGRAM)
典型场景 浏览器访问网站、微信聊天、远程登录SSH、视频直播
编程特点 地址族使用 AF_INET(IPv4)或 AF_INET6(IPv6),最常用的套接字类型

三、标准套接字编程的头文件

复制代码
// 标准套接字编程包含
#include <sys/socket.h>   // socket(), bind(), connect() 等
#include <netinet/in.h>   // sockaddr_in, sockaddr_in6 结构体
#include <arpa/inet.h>    // inet_pton(), inet_ntop() 等地址转换函数
#include <string.h>       // memset()
#include <unistd.h>        // close()
头文件 作用 关键函数/结构体
<sys/socket.h> 提供套接字基础API socket(), bind(), connect(), listen(), accept(), send(), recv()
<netinet/in.h> 定义IPv4/IPv6地址结构 struct sockaddr_in (IPv4), struct sockaddr_in6 (IPv6)
<arpa/inet.h> 地址转换函数(字符串↔二进制) inet_pton() (字符串→二进制), inet_ntop() (二进制→字符串)
<string.h> 内存操作(如初始化结构体) memset(), memcpy()
<unistd.h> 系统调用(如关闭套接字) close()

四、sockaddr****结构

核心设计思想:通用接口与具体实现

Socket API 的设计者面临一个问题:如何用一套统一的函数处理不同协议的地址

  • IPv4 地址是 4 字节 + 2 字节端口
  • IPv6 地址是 16 字节 + 2 字节端口
  • Unix Domain Socket 是文件路径字符串

解决方案使用强制类型转换和地址族字段

  1. 定义一个通用的基类结构体 struct sockaddr
  2. 定义具体的派生类结构体 struct sockaddr_in (IPv4)、struct sockaddr_in6 (IPv6)
  3. 所有 API 函数都接受 struct sockaddr * 类型的指针
  4. 程序员在使用时,将具体的结构体指针强制转换为通用指针传给 API
  5. API 内部通过检查第一个字段 sa_family 来判断实际传入的是哪种结构体,并解析数据

通用地址结构:struct sockaddr

这是所有 socket 地址结构的**"基类"**,主要用于函数参数的类型统一

成员 类型 说明
sa_family sa_family_t 地址族 (关键字段!)。标识协议类型: - AF_INET (IPv4) - AF_INET6 (IPv6) - AF_UNIX / AF_LOCAL (Unix Domain Socket)
sa_data char[14] 地址数据 。存放具体的地址信息(如IP、端口、路径等)。 注意 :程序不应直接访问 此数组,需通过具体结构体解析。

作用 :提供统一的指针类型 struct sockaddr *,使 bind()connect() 等函数能接受任何协议的地址

IPv4 专用地址结构:struct sockaddr_in

这是 IPv4 的**"派生类"** ,包含具体的 IPv4 地址信息。定义在头文件 <netinet/in.h>

成员 类型 说明
sin_family sa_family_t 必须设为 AF_INET 。对应基类的 sa_family
sin_port uint16_t 16位端口号必须使用网络字节序 (大端序)! 需用 htons() 函数转换。
sin_addr struct in_addr 32位 IPv4 地址必须使用网络字节序 ! 需用 htonl()inet_addr() 转换。
sin_zero char[8] 填充字段 。为了保持与 struct sockaddr 大小一致而存在。 必须用 memset 或 bzero 清零,无实际意义。

关键点:

  • 在代码中我们操作的是 sockaddr_in。
  • 传给 API 时强制转换为 (struct sockaddr *)&addr。
  • 字节序:sin_port 和 sin_addr 必须是网络字节序(大端序)
  • 必须清零

对 struct sockaddr_in 清零的核心原因在于:该结构体中的 sin_zero[8] 是内核强制要求的填充字段,必须全为0 ;由于结构体是分配在栈上的局部变量,其内存内容为随机的垃圾数据,若不通过 memset 或初始化显式清零,这些残留数据(尤其是 sin_zero 中的随机值)会被内核读取,导致 bind() 等系统调用不可预期地失败,同时也带来非确定性行为、调试困难和潜在的安全隐患

bzero

项目 说明
函数名 bzero
全称 Byte Zero(将字节区域清零)
头文件 <strings.h> (注意:不是 <string.h>
原型 void bzero(void *s, size_t n);
参数1 s:指向要清零的内存区域的指针
参数2 n:要清零的字节数
返回值 无返回值(void
功能 将指定内存区域的前 n 个字节设置为 0
起源 BSD 系统引入的函数,起源于早期 Unix
标准状态 废弃函数(在 POSIX.1-2001 中标记为过时,POSIX.1-2008 中已移除)

与 memset 的对比

对比维度 bzero memset
原型 void bzero(void *s, size_t n); void *memset(void *s, int c, size_t n);
功能 将内存区域清零 将内存区域设置为指定值
参数2 只填 0(固定) 可填任意字节(如 0、'\0' 等)
返回值 返回指向 s 的指针
头文件 <strings.h> <string.h>
标准 废弃(legacy) ANSI C / ISO C 标准
可移植性 差(非标准) 好(所有平台支持)

IPv4 地址封装:struct in_addr

专门用于表示 32 位 IPv4 地址的结构体

| 结构体 | 作用 | 关键字段 | 字节序要求 |
| struct sockaddr | 通用基类(API接口用) | sa_family | 无 |
| struct sockaddr_in | IPv4 专用结构 | sin_family, sin_port, sin_addr | 端口和IP必须网络字节序 |

struct in_addr 封装 IPv4 地址 s_addr (32位整数) 必须网络字节序
类型 变量 说明
struct in_addr sin_addr 封装 IPv4 地址
uint32_t s_addr(sin_addr的成员) 32位 IPv4 地址 (网络字节序)。 注意 :不能直接赋值为 192.168.1.1 这样的十进制数,必须使用 htonl()inet_addr()

IP地址设计为结构体的原因:

原因 说明
历史兼容性 早期的 Unix 实现中,IP 地址可能有多种表达方式,结构体便于扩展
抽象与封装 隐藏底层数据类型,允许未来修改实现而不影响上层代码
语义清晰 明确表示这是一个 IP 地址,而不是一个普通的 32 位整数
支持多种地址族 为后续支持 IPv6 等不同长度的地址奠定基础
操作便利性 可以方便地定义针对 IP 地址的操作函数

整数IP<--->字符串IP

整数IP与字符串IP转换的核心思想是利用IP地址的32位二进制与点分十进制的一一对应关系 。转换时有两种方法:一是通过位运算 直接操作整数的各字节(整数→字符串:右移提取各字节;字符串→整数:左移拼接各字节),容易实现;二是利用内存映射定义四字段结构体 ,通过指针强转直接访问各字节,较难实现

实际开发中应优先使用标准库函数inet_pton/inet_ntop

复制代码
// 网络字节序整数 -> 点分十进制字符串
std::string ip_to_str(uint32_t net_ip) noexcept {
    uint8_t bytes[4] = {
        static_cast<uint8_t>(net_ip >> 24),
        static_cast<uint8_t>(net_ip >> 16),
        static_cast<uint8_t>(net_ip >> 8),
        static_cast<uint8_t>(net_ip)
    };
    
    char buffer[16] = {0};
    snprintf(buffer, sizeof(buffer), "%hhu.%hhu.%hhu.%hhu",
             bytes[0], bytes[1], bytes[2], bytes[3]);
    return buffer;
}

// 点分十进制字符串 -> 网络字节序整数
// 返回 bool 表示成功/失败,结果通过参数返回
bool str_to_ip(const std::string& ip, uint32_t& result) noexcept {
    unsigned int a, b, c, d;  // 用unsigned避免溢出
    int pos = 0;
    
    // 使用 %n 检查完全匹配
    if (sscanf(ip.c_str(), "%u.%u.%u.%u%n", 
               &a, &b, &c, &d, &pos) == 4 && 
        pos == static_cast<int>(ip.length()) &&
        (a | b | c | d) <= 0xFF) {  // 快速范围检查
        result = (a << 24) | (b << 16) | (c << 8) | d;
        return true;
    }
    return false;
}

inet_addr

项目 说明
函数名 inet_addr
全称 Internet Address (IPv4)
头文件 <arpa/inet.h> (Linux/Unix) <winsock2.h> (Windows)
原型 in_addr_t inet_addr(const char *cp);
参数 cp:点分十进制格式的 IPv4 地址字符串(如 "192.168.1.100"
返回值 成功:返回 32位IPv4地址 (网络字节序) 失败:返回 INADDR_NONE (通常是 0xFFFFFFFF,即 -1255.255.255.255)
功能 将点分十进制字符串转换为 32位网络字节序的二进制值
适用场景 sockaddr_in 结构体中填充 sin_addr.s_addr 字段

inet_pton

项目 说明
函数名 inet_pton
全称 Internet Presentation to Network (地址呈现格式转网络格式)
头文件 <arpa/inet.h> (Linux/Unix) <ws2tcpip.h> (Windows)
原型 int inet_pton(int af, const char *src, void *dst);
参数1 af:地址族,AF_INET (IPv4) 或 AF_INET6 (IPv6)
参数2 src:点分十进制 (IPv4) 或十六进制 (IPv6) 格式的地址字符串
参数3 dst:指向存储二进制结果的指针(struct in_addr *struct in6_addr *
返回值 1 :成功 0 :输入的字符串不是有效的IP地址格式 -1:地址族不支持或发生错误(并设置 errno)
功能 将人可读的IP地址字符串转换为网络字节序的二进制格式
适用场景 现代网络编程中替代 inet_addrinet_aton,填充 sin_addrsin6_addr 结构

inet_ntoa

项目 说明
函数名 inet_ntoa
全称 Internet Network to Address(网络格式转地址字符串)
头文件 <arpa/inet.h> (Linux/Unix) <winsock2.h> (Windows)
原型 char *inet_ntoa(struct in_addr in);
参数 instruct in_addr 结构体,包含网络字节序的32位IPv4地址
返回值 返回指向点分十进制字符串的指针(如 "192.168.1.100"
功能 将网络字节序的二进制IP地址转换为点分十进制字符串
适用场景 打印IP地址、日志记录、调试输出
线程安全 非线程安全(使用静态缓冲区)

inet_ntop

项目 说明
函数名 inet_ntop
全称 Internet Network to Presentation(网络格式转呈现格式)
头文件 <arpa/inet.h> (Linux/Unix) <ws2tcpip.h> (Windows)
原型 const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
参数1 af:地址族,AF_INET (IPv4) 或 AF_INET6 (IPv6)
参数2 src:指向二进制IP地址的指针(struct in_addr *struct in6_addr *
参数3 dst:调用者提供的缓冲区,用于存储转换后的字符串
参数4 size:缓冲区大小(字节数)
返回值 成功:返回 dst 指针 失败:返回 NULL,并设置 errno
功能 将网络字节序的二进制IP地址转换为人可读的字符串格式(点分十进制或十六进制)
适用场景 打印IP地址、日志记录、调试输出、响应构造
线程安全 线程安全(调用者提供缓冲区)

结构体关系与强制转换示例

复制代码
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

// 1. 定义并初始化 IPv4 具体结构体
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr)); // 清零,包括 sin_zero

server_addr.sin_family = AF_INET;             // 地址族:IPv4
server_addr.sin_port = htons(8080);           // 端口号:8080 (主机序转网络序)
server_addr.sin_addr.s_addr = inet_addr("192.168.1.100"); // IP地址 (字符串转网络序)
// 或者: server_addr.sin_addr.s_addr = htonl(0xC0A80164);

// 2. 调用 bind 时,强制转换为通用指针
// 注意第三个参数是具体结构体的大小
int ret = bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
相关推荐
网安情报局1 小时前
除了 CDN,DDoS 攻击还有哪些更有效的防护方式?
网络
Promise微笑1 小时前
2026年国产替代油介损测试仪:油介损全场景解决方案与技术演进
大数据·网络·人工智能
蜡台2 小时前
Python包管理工具pip完全指南-----2
linux·windows·python
Ujimatsu2 小时前
虚拟机安装Debian 13.x及其常用软件(2026.4)
linux·运维·ubuntu
千百元2 小时前
zookeeper启不来了
linux·zookeeper·debian
AnalogElectronic4 小时前
linux 测试网络和端口是否连通的命令详解
linux·网络·php
Edward111111114 小时前
4月28日防火墙问题
linux·运维·服务器
Rust研习社5 小时前
使用 Axum 构建高性能异步 Web 服务
开发语言·前端·网络·后端·http·rust
灰子学技术5 小时前
Envoy HTTP 流量层面的 Metric 指标分析
网络·网络协议·http
上海云盾-小余5 小时前
海外恶意 UDP 攻击溯源:分层封禁策略与业务兼容平衡方案
网络·网络协议·udp