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相关套接字的实现,以及最后就是二者的实现了,希望让大家有所收获!

相关推荐
会飞的土拨鼠呀10 分钟前
Ubuntu系统缺少 iptables 工具
linux·运维·ubuntu
前端玖耀里10 分钟前
详细介绍Linux命令dig和nslookup
linux·运维·服务器
呱呱巨基10 分钟前
Linux 第一个系统程序 进度条
linux·c++·笔记·学习
星期五不见面18 分钟前
jetson naon super使用 sudo /opt/nvidia/jetson-io/jetson-io.py 界面闪退
linux·运维·服务器
Coder个人博客24 分钟前
Linux6.19-ARM64 mm hugetlbpage子模块深入分析
linux·安全·车载系统·系统架构·系统安全·鸿蒙系统·安全架构
FreeBuf_29 分钟前
AutoPentestX:面向 Linux 系统的自动化渗透测试工具包
linux·运维·自动化
EverydayJoy^v^34 分钟前
RH134简单知识点——第11章—— 管理网络安全
linux·网络·web安全
leiming638 分钟前
Qt视频监控系统开发实战:从视频捕获到照片管理
linux·数码相机·音视频
网硕互联的小客服39 分钟前
linux服务器忘记远程端口怎么办?如何找回?
linux·运维·服务器
历程里程碑40 分钟前
Linux 16 环境变量
linux·运维·服务器·开发语言·数据库·c++·笔记