Linux(10)(中)

https://blog.csdn.net/qscftqwe/article/details/156236643

上节课链接,大家可以点击进行观看

一.sockaddr结构

socket API 是一层抽象的网络编程接口 ,适用于各种底层网络协议,如 IPv4、IPv6 ,以及后面要讲的 UNIX Domain Socket 。然而,各种网络协议的地址格式并不相同

  • Pv4 和 IPv6 的地址格式定义在 <netinet/in.h> 中:IPv4 使用 sockaddr_in 结构体 ,包含 16 位地址族、16 位端口号和 32 位 IP 地址
  • 地址族常量 AF_INETAF_INET6 用于标识协议类型
  • socket API 使用 struct sockaddr * 作为通用地址指针 ,实际使用时需根据协议族强制转换为具体类型 (如 sockaddr_in*)。
  • 为支持多协议,可使用 sockaddr_storage 作为缓冲区 ,并通过 sa_family 字段判断地址类型后再进行安全转换

1.1 sockaddr结构

cpp 复制代码
​// 通用套接字地址结构 用于网络 API 的地址参数
// 实际使用时通常用 sockaddr_in(IPv4)或 sockaddr_in6(IPv6)填充
#include <sys/socket.h>

struct sockaddr 
{
    sa_family_t sa_family;  // 地址族(如 AF_INET)
    char        sa_data[14]; // 地址数据(协议相关)
};

// 说明:
//   该结构是抽象基类 不直接使用
//   调用 bind/connect/accept 时 将具体地址结构(如 sockaddr_in)强制转换为 sockaddr *


​

1.2 sockaddr_in结构

cpp 复制代码
// IPv4 专用套接字地址结构 用于 bind/connect/accept 等函数
// 实际传参时需强制转换为 struct sockaddr *
#include <netinet/in.h>

struct sockaddr_in 
{
    sa_family_t     sin_family;   // 地址族 必须为 AF_INET
    in_port_t       sin_port;     // 端口号(网络字节序)
    struct in_addr  sin_addr;     // IPv4 地址(网络字节序)
    char            sin_zero[8];  // 填充字段 保持与 sockaddr 长度一致
};

// 成员说明:
//   sin_family - 固定设为 AF_INET
//   sin_port   - 用 htons() 转换主机端口到网络序
//   sin_addr   - 用 inet_pton() 或 INADDR_ANY 设置 IP


// 表示 IPv4 地址的结构体 通常作为 sockaddr_in 的成员使用
// 实际地址以网络字节序(大端)存储
#include <netinet/in.h>
struct in_addr 
{
    in_addr_t s_addr;  // 32 位 IPv4 地址(网络字节序)
};

// 说明:
//   s_addr 是 uint32_t 类型 存储如 0x0100007f(即 127.0.0.1)
//   应使用 inet_pton() / inet_ntop() 或 htonl() 等函数转换

二.地址转换函数

2.1 字符串转in_addr的函数

1.inet_addr

cpp 复制代码
// 将点分十进制 IPv4 字符串(如 "192.168.1.1")转换为 32 位网络字节序整数
// 已过时 建议改用 inet_pton
#include <arpa/inet.h>

in_addr_t inet_addr(const char *strptr);

// 参数:
//   strptr - 指向 IPv4 地址字符串

// 返回值:
//   成功:返回网络字节序的 32 位地址(in_addr_t)
//   失败或输入为 "255.255.255.255":返回 INADDR_NONE(通常为 -1)

// 案例:
    in_addr_t ip = inet_addr("192.168.1.1");

2.inet_aton

cpp 复制代码
// 将点分十进制 IPv4 字符串安全地转换为 struct in_addr
// 推荐替代 inet_addr 因其能明确区分错误与合法地址
#include <arpa/inet.h>

int inet_aton(const char *strptr, struct in_addr *addrptr);

// 参数:
//   strptr   - 指向 IPv4 地址字符串
//   addrptr  - 指向 struct in_addr 的指针 用于存储结果

// 返回值:
//   成功:返回 1
//   失败:返回 0

// 案例:
    struct in_addr ip;
    if (inet_aton("192.168.1.1", &ip)) {
        // 转换成功
    }

3.inet_pton

cpp 复制代码
// 通用地址字符串转二进制(支持 IPv4 和 IPv6)
// 现代推荐方式 可移植性好
#include <arpa/inet.h>

int inet_pton(int family, const char *strptr, void *addrptr);

// 参数:
//   family   - 地址族(AF_INET 或 AF_INET6)
//   strptr   - 指向 IP 字符串(如 "192.168.1.1" 或 "::1")
//   addrptr  - 指向目标地址缓冲区(如 &in_addr 或 &in6_addr)

// 返回值:
//   成功:返回 1
//   输入无效:返回 0
//   不支持的 family:返回 -1

// 案例:
    struct in_addr ip4;
    inet_pton(AF_INET, "192.168.1.1", &ip4);

2.2 in_addr转字符串的函数

1.inet_ntoa

cpp 复制代码
// 将 IPv4 地址(struct in_addr)转换为点分十进制字符串(如 "192.168.1.1")
// 返回静态缓冲区指针 非线程安全 且后续调用会覆盖结果
#include <arpa/inet.h>

char *inet_ntoa(struct in_addr inaddr);

// 参数:
//   inaddr - 要转换的 IPv4 地址(网络字节序)

// 返回值:
//   成功:返回指向静态字符串的指针(如 "192.168.0.1")
//   失败:行为未定义(通常仍返回字符串)

// 案例:
    struct in_addr ip;
    ip.s_addr = htonl(INADDR_LOOPBACK);
    printf("IP: %s\n", inet_ntoa(ip));

2.inet_ntop

cpp 复制代码
// 通用二进制地址转字符串(支持 IPv4 和 IPv6)
// 线程安全 需提供输出缓冲区 推荐替代 inet_ntoa
#include <arpa/inet.h>

const char *inet_ntop(
    int family,
    const void *addrptr,
    char *strptr,
    size_t len
);

// 参数:
//   family   - 地址族(AF_INET 或 AF_INET6)
//   addrptr  - 指向二进制地址(如 &in_addr 或 &in6_addr)
//   strptr   - 输出缓冲区(用于存放结果字符串)
//   len      - 缓冲区大小(IPv4 至少 INET_ADDRSTRLEN,IPv6 至少 INET6_ADDRSTRLEN)

// 返回值:
//   成功:返回 strptr
//   失败:返回 NULL 并设置 errno

// 案例:
    char str[INET_ADDRSTRLEN];
    inet_ntop(AF_INET, &ip.s_addr, str, sizeof(str));

inet_ntoa相关知识

  • inet_ntoa 将 IPv4 地址转换为点分十进制字符串,其返回值是指向内部静态缓冲区的指针
  • 该缓冲区位于静态存储区,无需手动释放
  • 多次调用会覆盖之前的结果
  • 多线程环境下不安全

推荐使用 inet_ntop 替代:它由调用者提供输出缓冲区,线程安全,且同时支持 IPv4 和 IPv6。

三.UDP

3.1 服务器

cpp 复制代码
// 1. 创建UDP套接字
int sockfd_ = socket(AF_INET, SOCK_DGRAM, 0);

// 2. 配置服务器地址结构
struct sockaddr_in local;
memset(&local, 0, sizeof(local));
local.sin_family = AF_INET;
local.sin_port = htons(port_);                // 转换端口号为网络字节序
local.sin_addr.s_addr = inet_addr(ip_.c_str()); // 转换IP地址

// 3. 绑定套接字到本地地址
int ret = bind(sockfd_, (struct sockaddr*)&local, sizeof(local)); // 修正变量名sockfd_

// 4. 接收数据
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
               struct sockaddr *src_addr, socklen_t *addrlen);

// 5. 发送数据
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
              const struct sockaddr *dest_addr, socklen_t addrlen);

3.2 客户端

补充知识:

  • 客户端通常无需显式调用 bind() ,操作系统会在需要时(TCP 在 connect() 时,UDP 在首次发送时自动为其分配一个唯一的临时端口
  • 该临时端口同样受"端口独占 "规则约束:在同一协议和本地地址下,不能被多个 socket 同时绑定
  • 客户端端口号无需关心 ,只要在本机唯一即可 ,用于服务器回传数据
cpp 复制代码
// 1. 创建UDP套接字
fd_ = socket(AF_INET, SOCK_DGRAM, 0);

// 2. 配置服务器地址结构
struct sockaddr_in server;
memset(&server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(serverport);               // 端口号转网络字节序
inet_pton(AF_INET, serverip.c_str(), &(server.sin_addr)); // IP地址转换

// 3. 发送数据
string message;
char buffer[1024];

sendto(fd_, message.c_str(), message.size(), 0, (struct sockaddr*)&server, sizeof(server));

// 4. 接收数据
struct sockaddr_in temp;
socklen_t len = sizeof(temp);

ssize_t s = recvfrom(fd_, buffer, 1023, 0,
					(struct sockaddr*)&temp, &len); // 保留1字节给\0

if (s > 0) buffer[s] = '\0'; // 确保字符串终止

四.补充知识

端口知识

  • 0--1023:特权端口,需 root 权限;
  • 1024--49151:注册端口,普通用户可用,许多数据库/服务在此范围
  • 49152--65535 :临时端口,由系统自动分配。
    建议服务器选用 1024 以上且未被占用的端口,开发中常用 8000+ 以避免冲突,但非强制。

UDP 套接字性质

UDP 套接字支持同时收发数据,是全双工的。

环回地址

环回地址(如 127.0.0.1)的数据包在内核中被拦截并重定向回协议栈上层,不经过物理网络 ,但完整经历协议栈处理

好了今天的知识就讲到这里了,今天的内容还是比较多的,后面我会带来TCP相关套接字的实现,以及最后就是二者的实现了,希望让大家有所收获!

相关推荐
牛奶咖啡132 小时前
shell脚本编程(六)
linux·shell脚本编程·shell中的判断·if/else结构·if/elif/else结构·case选择语句·shell各种判断的语法及示例
sim20202 小时前
创建FTP账号
linux
Ha_To2 小时前
2026.1.12 Linux提升账户权限
linux·运维·服务器
OopspoO2 小时前
Linux内存分析工具
linux·运维
oMcLin2 小时前
如何在Ubuntu 20.04上配置并调优Kubernetes集群,确保在多租户环境下的高可用性与资源分配?
linux·ubuntu·kubernetes
石头5303 小时前
Service 详解
linux
小鸡脚来咯3 小时前
Linux 服务器问题排查指南(面试标准回答)
linux·服务器·面试
末日汐3 小时前
磁盘与文件系统
linux·运维·数据库
水天需0103 小时前
Linux PS4 环境变量详解
linux