当前位置: 首页 > news >正文

Linux网络编程基础(地址结构)

一、Socket地址的本质

Socket最初的含义是一个IP地址和端口对(ip, port),它唯一地表示了使用TCP通信的一端,这就是Socket地址。在网络编程中,需要一种标准化的数据结构来描述这个地址对,这就引出了各种地址结构体。

二、通用Socket地址结构

2.1 sockaddr结构体

Socket网络编程接口中表示socket地址的是结构体sockaddr,其定义如下:

#include <bits/socket.h> struct sockaddr { sa_family_t sa_family; // 地址族类型 char sa_data[14]; // 存放socket地址值(14字节) };
  • sa_family:地址族类型,通常与协议族类型对应,如AF_INET表示IPv4协议
  • sa_data:用于存放具体的socket地址值,但只有14字节

2.2 为什么需要通用地址结构?

socket网络编程接口中使用的是sockaddr作为地址参数类型,但不同的协议族(如IPv4、IPv6、UNIX域)有不同的地址格式和长度。14字节的sa_data根本无法完全容纳多数协议族的地址值(例如IPv6地址就需要26字节)。因此,Linux定义了新的通用socket地址结构体sockaddr_storage

#include <bits/socket.h> struct sockaddr_storage { sa_family_t sa_family; unsigned long int __ss_align; char __ss_padding[128 - sizeof(__ss_align)]; };

这个结构体不仅提供了足够大的空间(128字节)用于存放地址值,而且是内存对齐的(这是__ss_align成员的作用)。

三、专用Socket地址结构

通用socket地址结构体在设置与获取IP地址和端口号时需要执行繁琐的位置操作,因此Linux为各个协议族提供了专门的socket地址结构体。

3.1 IPv4专用地址结构:sockaddr_in

这是最常用的地址结构,用于IPv4网络通信:

struct sockaddr_in { sa_family_t sin_family; // 地址族:AF_INET u_int16_t sin_port; // 端口号,要用网络字节序表示 struct in_addr sin_addr; // IPv4地址结构体 }; struct in_addr { u_int32_t s_addr; // IPv4地址,要用网络字节序表示 };

各成员的作用:

  • sin_family:必须设置为AF_INET,表示IPv4协议族
  • sin_port:16位端口号,需要使用htons()函数转换为网络字节序
  • sin_addr.s_addr:32位IP地址,需要使用htonl()inet_addr()等函数转换为网络字节序

3.2 IPv6专用地址结构:sockaddr_in6

用于IPv6网络通信:

struct sockaddr_in6 { sa_family_t sin6_family; // 地址族:AF_INET6 u_int16_t sin6_port; // 端口号,要用网络字节序表示 u_int32_t sin6_flowinfo; // 流信息,应设置为0 struct in6_addr sin6_addr; // IPv6地址结构体 u_int32_t sin6_scope_id; // scope ID,尚处于实验阶段 }; struct in6_addr { unsigned char sa_addr[16]; // IPv6地址,要用网络字节序表示 };

3.3 UNIX本地域协议地址结构:sockaddr_un

用于本地进程间通信(UNIX域套接字):

#include <sys/un.h> struct sockaddr_un { sa_family_t sin_family; // 地址族:AF_UNIX char sun_path[108]; // 文件路径名 };

3.4 地址结构的实际使用

所有专用socket地址类型的变量在实际使用中都需要转化为通用socket地址类型sockaddr(强制转换即可),因为所有的socket编程接口使用的地址参数类型都是sockaddr。例如:

struct sockaddr_in server_addr; // 初始化server_addr... bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));

四、主机字节序与网络字节序

4.1 字节序的概念

现代CPU的累加器一次都能装载至少4字节(32位机),这4个字节在内存中的排列顺序会影响它们被累加器装载成的整数的值,这就是字节序问题

字节序分为两种:

  • 大端字节序(Big Endian):整数的高位字节存储在内存的低地址处,低位字节存储在内存的高地址处
  • 小端字节序(Little Endian):整数的高位字节存储在内存的高地址处,低位字节存储在内存的低地址处

现代PC大多采用小端字节序,因此小端字节序又被称为主机字节序

4.2 网络字节序的由来

当格式化的数据在两台使用不同字节序的主机之间直接传递时,接收端必然错误地解释数据。解决问题的办法是:发送端总是把要发送的数据转化成大端字节序后再发送,而接收端知道对方传送过来的数据总是采用大端字节序,所以接收端可以根据自身采用的字节序决定是否对接收到的数据进行转换(小端机转换,大端机不转换)。因此,大端字节序也称为网络字节序

4.3 字节序转换函数

Linux提供了4个函数来完成主机字节序和网络字节序之间的转换:

#include <arpa/inet.h> uint32_t htonl(uint32_t hostlong); // 主机字节→网络字节,32位长整型 uint16_t htons(uint16_t hostshort); // 主机字节→网络字节,16位短整型 uint32_t ntohl(uint32_t netlong); // 网络字节→主机字节,32位长整型 uint16_t ntohs(uint16_t netshort); // 网络字节→主机字节,16位短整型

函数名含义:

  • h表示host(主机),n表示network(网络)
  • l表示32位长整数(long),s表示16位短整数(short)
  • 例如htonl表示将32位的长整数从主机字节序转换为网络字节序

实际应用中:

  • 长整型函数(htonl/ntohl)通常用来转换IP地址
  • 短整型函数(htons/ntohs)用来转换端口号

如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回;如果主机是大端字节序,这些函数不做转换,将参数原封不动地返回。

五、IP地址转换函数

5.1 传统IPv4地址转换函数

以下函数用于将点分十进制字符串表示的IPv4地址和网络字节序整数表示的IPv4地址之间进行转换:

#include <arpa/inet.h> in_addr_t inet_addr(const char *strptr); // 字符串→32位整数 int inet_aton(const char *cp, struct in_addr *inp); // 字符串→结构体 char *inet_ntoa(struct in_addr in); // 结构体→字符串
  • inet_addr():将点分十进制字符串IP转换为32位网络字节序整数,失败返回INADDR_NONE
  • inet_aton():功能同inet_addr(),但将结果存储于inp指向的地址结构中,成功返回1,失败返回0
  • inet_ntoa():将32位整数IP转换为点分十进制字符串,注意:该函数内部用一个静态变量存储转化结果,函数返回值指向该静态内存,因此inet_ntoa不可重入的(线程不安全)

5.2 通用地址转换函数(推荐使用)

以下函数同时适用于IPv4和IPv6地址,且是可重入的(线程安全):

#include <arpa/inet.h> int inet_pton(int af, const char *src, void *dst); const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
  • inet_pton():将字符串IP地址转换为二进制格式,af指定协议族(AF_INETAF_INET6),成功返回1,无效地址返回0,错误返回-1
  • inet_ntop():将二进制地址转换为字符串格式,需要指定输出缓冲区的大小size,返回指向dst的指针

推荐使用inet_ptoninet_ntop替代传统函数,因为它们更安全、更通用。

六、地址结构的使用示例

以下是一个典型的TCP服务器端初始化地址结构的示例:

struct sockaddr_in server_addr; // 1. 清零结构体 memset(&server_addr, 0, sizeof(server_addr)); // 2. 设置地址族 server_addr.sin_family = AF_INET; // 3. 设置端口号(转换为网络字节序) server_addr.sin_port = htons(8080); // 4. 设置IP地址(绑定所有本地网卡) server_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 或绑定指定IP:inet_pton(AF_INET, "192.168.1.100", &server_addr.sin_addr); // 5. 绑定地址到套接字 bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));

七、总结

Linux网络编程中的地址结构体系可以分为三个层次:

层次结构体用途
通用地址sockaddr作为所有socket API的参数类型
扩展通用地址sockaddr_storage提供足够的空间容纳所有协议族的地址
专用地址sockaddr_in/sockaddr_in6/sockaddr_un分别用于IPv4、IPv6、UNIX域通信

在编程实践中,程序员通常使用专用地址结构(如sockaddr_in)来设置地址信息,然后通过强制类型转换将其传递给接受通用地址结构参数的API函数。同时,务必注意使用字节序转换函数(htonshtonl等)和IP地址转换函数(推荐inet_pton/inet_ntop)来确保数据的正确解释。

http://www.jsqmd.com/news/881438/

相关文章:

  • 机器学习加速等离子体仿真:从初始条件预测到PIC计算效率提升
  • 2026年4月目前有名的校车回收公司推荐,五菱校车/旧校车/宇通二手校车/窄车身幼儿校车/福田校车,校车供应商推荐 - 品牌推荐师
  • 机器人异常检测实战:基于系统日志的LR、SVM与自编码器模型对比
  • 构造数据类型
  • AODV协议智能增强:多模型机器学习提升蓝牙Mesh网络路由可靠性
  • Rockchip Debian编译卡在QEMU?别慌,可能是Ubuntu 18.04的锅(附升级20.04避坑指南)
  • 安卓So层Hook实战:ARM64函数定位与参数还原五步法
  • 告别虚拟机:在龙芯3A6000真机上流畅运行统信UOS的配置心得与性能调优建议
  • 2026年质量好的油缸修复专用珩磨机可靠供应商推荐 - 行业平台推荐
  • Word2016受保护视图报错原因与安全放行指南
  • Java NIO 连接状态守卫:AlreadyConnectedException 源码深度剖析与 SocketChannel 生命周期契约
  • 在Ubuntu 22.04上,用SSH和HTTPS两种方式搞定OpenHarmony 4.1 Release源码下载(附工具链配置)
  • 粒子物理分析中类别权重对机器学习分类器性能与物理结果的影响
  • UABEA:Unity跨平台资源编辑与二进制解析工具深度指南
  • HPE DL560 Gen10服务器装系统踩坑实录:Windows Server 2012 R2下P816i-a SR阵列卡驱动安装全流程
  • Java中的接口
  • AssetStudio深度指南:Unity资源提取与二进制结构解析
  • 在Ubuntu 14.04上为老旧系统(如XP)搭建现代Web服务栈:Apache 2.4.59 + OpenSSL 1.1.1w + PHP 8.3.6 保姆级配置指南
  • 重赏之下必有勇夫的科学依据找到了:《Science》发现超级大奖励可“开挂”学习,多巴胺是幕后功臣
  • 深入Linux内核链表:从of_property_read_bool看设备树属性的组织与查找
  • r0capture安卓抓包原理:绕过证书固定提取SSL密钥
  • AI Agent Harness模型推理缓存优化
  • 机器学习加速超导材料发现:从梯度提升回归到DFT验证的完整工作流
  • 保姆级教程:Ubuntu 20.04下RTL8111/8168网卡驱动安装与自动加载(实测有效)
  • Unity深度感知动态模糊系统:分层控制与UI隔离实战
  • 混沌系统预测:输入长度如何影响模型误差与稳定性
  • Rust Web框架对比:Axum、Rocket、Warp深度解析
  • DaCe AD:打造不挑食的高性能自动微分引擎,加速科学计算梯度计算
  • 物理信息机器学习:融合物理定律与数据,革新燃烧模拟与优化
  • OpenClaw+SecGPT-14B:渗透测试上下文编排与AI报告生成实战