您的位置:首页 > 财经 > 金融 > 供应链管理有限公司_做网站的公司没给做好能起诉吗_搜索引擎优化不包括_免费搜索引擎推广方法有哪些

供应链管理有限公司_做网站的公司没给做好能起诉吗_搜索引擎优化不包括_免费搜索引擎推广方法有哪些

2025/6/23 18:12:23 来源:https://blog.csdn.net/SEU123WW/article/details/146962379  浏览:    关键词:供应链管理有限公司_做网站的公司没给做好能起诉吗_搜索引擎优化不包括_免费搜索引擎推广方法有哪些
供应链管理有限公司_做网站的公司没给做好能起诉吗_搜索引擎优化不包括_免费搜索引擎推广方法有哪些

《UNIX网络编程卷1:套接字联网API》第3章 套接字编程简介


3.1 套接字地址结构:网络通信的基石

套接字地址结构是网络编程的核心数据结构,定义了通信实体的网络标识与端口信息。不同协议族(如IPv4、IPv6、Unix域)拥有独立的地址结构,但均通过通用结构 struct sockaddr 实现类型统一。

3.1.1 IPv4地址结构(sockaddr_in)
#include <netinet/in.h>struct sockaddr_in {uint8_t         sin_len;     // 结构体长度(BSD系统特有)sa_family_t     sin_family;  // 地址族(AF_INET)in_port_t       sin_port;    // 16位端口号(网络字节序)struct in_addr  sin_addr;    // 32位IPv4地址(网络字节序)char            sin_zero[8]; // 填充字段(全零)
};struct in_addr {in_addr_t s_addr; // 32位IPv4地址(网络字节序)
};

关键点

  • sin_portsin_addr 必须使用网络字节序(大端模式);
  • sin_zero 字段用于填充,确保结构体与 sockaddr 对齐;
  • 实际编程中需使用 bzeromemset 初始化结构体,避免内存残留。
3.1.2 IPv6地址结构(sockaddr_in6)
struct sockaddr_in6 {uint8_t         sin6_len;      // 结构体长度sa_family_t     sin6_family;   // 地址族(AF_INET6)in_port_t       sin6_port;     // 16位端口号(网络字节序)uint32_t        sin6_flowinfo; // 流标签(QoS支持)struct in6_addr sin6_addr;     // 128位IPv6地址uint32_t        sin6_scope_id; // 作用域标识符(链路本地地址使用)
};struct in6_addr {uint8_t s6_addr[16]; // 128位IPv6地址
};

特点

  • 支持更大的地址空间(128位)和流标签(用于区分数据流优先级);
  • 作用域标识符用于处理链路本地地址(如 fe80::1%eth0)。
3.1.3 通用地址结构(sockaddr与sockaddr_storage)
struct sockaddr {uint8_t     sa_len;     // 结构体长度sa_family_t sa_family;  // 地址族(AF_xxx)char        sa_data[14];// 协议地址(IP+端口)
};struct sockaddr_storage {sa_family_t ss_family;  // 地址族char        __ss_pad1[_SS_PAD1SIZE]; // 填充字段// ... 其他对齐字段(总长度通常为128字节)
};

设计意义

  • sockaddr 用于兼容旧版API,但无法容纳IPv6地址;
  • sockaddr_storage 是通用解决方案,可存储任意协议族的地址结构。

内存对齐图

偏移量 | 成员名称        | 大小(字节)  | 对齐方式         | 说明
-------------------------------------------------------------
0x00   | ss_family     | 2            | 自然对齐(2字节)| 地址族标识(如 AF_INET)
0x02   | __ss_pad1     | 6            | 填充至 8 字节对齐| 确保后续字段对齐
0x08   | __ss_align    | 8            | 8 字节对齐       | 强制 64 位对齐的占位符
0x10   | __ss_pad2     | 112          | 填充至 128 字节  | 剩余空间填充
-------------------------------------------------------------
总大小:128 字节

说明:sockaddr_storage通过填充字段确保内存对齐,避免平台兼容性问题。
以下是 sockaddr_storage 结构体的内存对齐图及详细分析,结合 Linux 和 Windows 系统的实现差异进行说明:

关键设计解析

  1. 多平台兼容性

    • Linuxss_family(2字节)后通过 __ss_pad1(6字节)填充至 8 字节边界,使 __ss_align(8字节)满足 64 位对齐。
    • Windowsss_family(2字节)后直接填充 48 字节(__ss_pad1),再放置 __ss_align(8字节),最后通过 __ss_pad2(72字节)填充至 128 字节。
  2. 对齐规则

    • 自然对齐:首个成员 ss_family 从偏移量 0 开始,占用 2 字节。
    • 强制对齐__ss_align 需对齐到 8 字节边界(编译器默认对齐数),确保后续字段(如 IPv6 地址)无错位访问。
    • 总大小约束:结构体总大小需为最大对齐数(8字节)的整数倍,故填充至 128 字节。
  3. 功能目的

    • 通用存储:128 字节空间可容纳 IPv4(16字节)、IPv6(28字节)等地址,避免动态类型判断。
    • 类型转换安全:强制对齐后,可直接将协议特定地址(如 sockaddr_in6)强制转换为 sockaddr_storage,避免内存越界。

实际应用场景

// 示例:接收任意协议地址
struct sockaddr_storage addr;
socklen_t addr_len = sizeof(addr);
recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&addr, &addr_len);// 根据 ss_family 处理不同协议
if (addr.ss_family == AF_INET) {struct sockaddr_in *ipv4_addr = (struct sockaddr_in*)&addr;// 处理 IPv4 地址
} else if (addr.ss_family == AF_INET6) {struct sockaddr_in6 *ipv6_addr = (struct sockaddr_in6*)&addr;// 处理 IPv6 地址
}

总结

sockaddr_storage 通过精心设计的填充字段和对齐机制,在以下方面达到平衡:

  • 空间效率:固定 128 字节,覆盖所有已知协议地址。
  • 访问安全性:强制对齐避免 CPU 非对齐访问异常。
  • 代码简洁性:统一接口处理多协议地址,降低网络编程复杂度。

3.2 字节序转换:跨越异构系统的桥梁

网络通信要求数据以**网络字节序(大端模式)**传输,主机字节序可能为小端(如x86)或大端(如PowerPC),需通过转换函数统一。

3.2.1 转换函数详解
#include <netinet/in.h>// 主机字节序 → 网络字节序
uint16_t htons(uint16_t hostshort); // 短整型(端口号)
uint32_t htonl(uint32_t hostlong);  // 长整型(IP地址)// 网络字节序 → 主机字节序
uint16_t ntohs(uint16_t netshort);
uint32_t ntohl(uint32_t netlong);

嵌入式应用场景

  • ARM设备(小端)与DSP处理器(大端)通信时需显式转换;
  • 示例:设置端口号 servaddr.sin_port = htons(8080);
3.2.2 结构体字段转换实践
struct sockaddr_in servaddr;
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(9999); // 转换端口号
inet_pton(AF_INET, "192.168.1.100", &servaddr.sin_addr); // IP地址转换

3.3 地址转换函数:字符串与二进制的互操作

IPv4/IPv6地址常需在可读字符串(如"192.168.1.1")和二进制格式间转换,推荐使用现代函数 inet_ptoninet_ntop

3.3.1 函数原型与参数解析
#include <arpa/inet.h>// 字符串 → 二进制(支持IPv4/IPv6)
int inet_pton(int af, const char *src, void *dst);// 二进制 → 字符串(需预分配缓冲区)
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);

参数说明

  • af:地址族(AF_INETAF_INET6);
  • src:输入数据指针;
  • dst:输出缓冲区;
  • size:缓冲区长度(推荐使用 INET_ADDRSTRLENINET6_ADDRSTRLEN)。

代码示例

char ipv4_str[INET_ADDRSTRLEN];
struct in_addr ipv4_addr;
inet_pton(AF_INET, "203.0.113.1", &ipv4_addr); // 字符串转二进制
inet_ntop(AF_INET, &ipv4_addr, ipv4_str, INET_ADDRSTRLEN); // 二进制转字符串
3.3.2 弃用函数警告
  • inet_addrinet_aton 仅支持IPv4,且无错误返回;
  • inet_ntoa 使用静态缓冲区,非线程安全。

3.4 套接字API核心函数解析
3.4.1 socket():创建通信端点
#include <sys/socket.h>
int socket(int domain, int type, int protocol);

参数详解

  • domain:协议族(如 AF_INETAF_INET6AF_UNIX);
  • type:套接字类型(SOCK_STREAMSOCK_DGRAM);
  • protocol:通常为0(自动选择默认协议)。

返回值

  • 成功:返回非负套接字描述符;
  • 失败:返回-1,设置 errno(如 EPROTONOSUPPORT 协议不支持)。

代码示例

int tcp_sock = socket(AF_INET, SOCK_STREAM, 0);
if (tcp_sock < 0) {perror("socket creation failed");exit(EXIT_FAILURE);
}
3.4.2 bind():绑定本地地址
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

关键点

  • 服务器必须调用 bind 以指定监听地址和端口;
  • 客户端通常无需显式绑定,由内核自动分配临时端口。

错误处理

  • EADDRINUSE:端口被占用;
  • EACCES:尝试绑定特权端口(<1024)无权限。

代码示例

struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); // 监听所有接口
servaddr.sin_port = htons(8080);if (bind(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) {perror("bind failed");close(sockfd);exit(EXIT_FAILURE);
}
3.4.3 listen():开启监听模式
int listen(int sockfd, int backlog);

参数解析

  • backlog:已完成连接队列(ESTABLISHED)的最大长度;
  • Linux内核4.3+后,backlog 仅限制已完成队列,由 net.core.somaxconn 定义上限。

最佳实践

if (listen(sockfd, SOMAXCONN) < 0) {perror("listen failed");close(sockfd);exit(EXIT_FAILURE);
}
3.4.4 accept():接受客户端连接
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

值-结果参数

  • addr:客户端地址结构指针;
  • addrlen:输入时为缓冲区大小,返回时为实际地址长度。

代码示例

struct sockaddr_in cliaddr;
socklen_t cliaddr_len = sizeof(cliaddr);
int connfd = accept(sockfd, (struct sockaddr*)&cliaddr, &cliaddr_len);
if (connfd < 0) {perror("accept failed");continue; // 非致命错误可继续运行
}
3.4.5 connect():发起连接请求
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

错误处理

  • ETIMEDOUT:服务器无响应(网络不通或防火墙拦截);
  • ECONNREFUSED:目标端口无服务监听;
  • EHOSTUNREACH:路由不可达。

代码示例

struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(8080);
inet_pton(AF_INET, "203.0.113.1", &servaddr.sin_addr);if (connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) {perror("connect failed");close(sockfd);exit(EXIT_FAILURE);
}

3.5 值-结果参数:内核与用户空间的协作

套接字API中,地址长度参数(如 acceptaddrlen)采用值-结果传递机制:

  • 输入:用户告知内核缓冲区大小,避免内存溢出;
  • 输出:内核返回实际地址长度,用户可验证协议类型。

示例分析

socklen_t len = sizeof(struct sockaddr_storage);
struct sockaddr_storage client_addr;
int connfd = accept(sockfd, (struct sockaddr*)&client_addr, &len);// 根据实际协议族处理连接
if (client_addr.ss_family == AF_INET) {struct sockaddr_in *ipv4_addr = (struct sockaddr_in*)&client_addr;// 处理IPv4地址
} else if (client_addr.ss_family == AF_INET6) {struct sockaddr_in6 *ipv6_addr = (struct sockaddr_in6*)&client_addr;// 处理IPv6地址
}

3.6 错误处理与包裹函数设计
3.6.1 系统调用错误处理模式

UNIX系统调用通过返回-1并设置 errno 指示错误类型:

if ((n = read(fd, buf, size)) < 0) {if (errno == EINTR) // 被信号中断goto retry;elseerr_sys("read error"); // 终止程序
}
3.6.2 包裹函数封装

通过包裹函数简化错误处理,提升代码可读性:

int Socket(int family, int type, int protocol) {int n;if ((n = socket(family, type, protocol)) < 0)err_sys("socket error");return n;
}void Connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {if (connect(sockfd, addr, addrlen) < 0)err_sys("connect error");
}

优点

  • 统一错误处理逻辑;
  • 减少代码冗余;
  • 支持调试日志插入。

3.7 实战:完整TCP回射服务器/客户端
3.7.1 服务器端代码
#include "unp.h"void str_echo(int sockfd) {ssize_t n;char buf[MAXLINE];while ((n = Read(sockfd, buf, MAXLINE)) > 0)Writen(sockfd, buf, n);
}int main() {int listenfd = Socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in servaddr;bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_port = htons(9999);Bind(listenfd, (SA*)&servaddr, sizeof(servaddr));Listen(listenfd, LISTENQ);for (;;) {int connfd = Accept(listenfd, NULL, NULL);if (fork() == 0) { // 子进程Close(listenfd);str_echo(connfd);Close(connfd);exit(0);}Close(connfd); // 父进程关闭连接套接字}
}
3.7.2 客户端代码
#include "unp.h"void str_cli(FILE *fp, int sockfd) {char sendline[MAXLINE], recvline[MAXLINE];while (Fgets(sendline, MAXLINE, fp) != NULL) {Writen(sockfd, sendline, strlen(sendline));if (Readline(sockfd, recvline, MAXLINE) == 0)err_quit("str_cli: server terminated prematurely");Fputs(recvline, stdout);}
}int main(int argc, char **argv) {if (argc != 2)err_quit("usage: tcpcli <IPaddress>");int sockfd = Socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in servaddr;bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(9999);Inet_pton(AF_INET, argv[1], &servaddr.sin_addr);Connect(sockfd, (SA*)&servaddr, sizeof(servaddr));str_cli(stdin, sockfd);exit(0);
}

3.8 图文说明与调试技巧
  1. TCP三次握手与套接字函数关联图
    在这里插入图片描述

    说明:socket()、bind()、listen()在服务器端准备连接,accept()阻塞等待SYN。

  2. 值-结果参数传递示意图

    +----------------+          +----------------+
    |   用户空间      |          |   内核空间      |
    | addrlen=16     |  --->    | 检查缓冲区大小  |
    | (输入值)       |  <---    | 设置实际长度    |
    +----------------+          +----------------+
    
  3. 常见错误排查表

    错误码原因解决方案
    EADDRINUSE端口被占用更换端口或设置SO_REUSEADDR
    ECONNREFUSED目标端口无服务检查服务器是否运行
    ENETUNREACH网络不可达检查路由与防火墙配置

3.9 本章小结与进阶习题

小结:本章深入解析了套接字地址结构、核心API函数及错误处理机制,通过完整的TCP案例演示了网络编程的基础流程,为后续高级主题奠定基础。

习题

  1. 修改回射服务器支持IPv6,并测试客户端连接;
  2. 实现基于UDP的简单聊天程序,支持多客户端通信;
  3. 使用Wireshark抓取TCP握手过程,分析序列号与确认号变化。

付费用户专属资源

  • 完整代码工程(含IPv4/IPv6双栈支持);

  • 套接字API调用流程图(矢量图);

  • 扩展阅读:《UNIX网络编程中的原子操作与线程安全》。

    | 目标端口无服务 | 检查服务器是否运行 |
    | ENETUNREACH | 网络不可达 | 检查路由与防火墙配置 |


3.9 本章小结与进阶习题

小结:本章深入解析了套接字地址结构、核心API函数及错误处理机制,通过完整的TCP案例演示了网络编程的基础流程,为后续高级主题奠定基础。

习题

  1. 修改回射服务器支持IPv6,并测试客户端连接;
  2. 实现基于UDP的简单聊天程序,支持多客户端通信;
  3. 使用Wireshark抓取TCP握手过程,分析序列号与确认号变化。

付费用户专属资源

  • 完整代码工程(含IPv4/IPv6双栈支持);
  • 套接字API调用流程图(矢量图);
  • 扩展阅读:《UNIX网络编程中的原子操作与线程安全》。

通过本章学习,读者将掌握套接字编程的核心技术,并能够开发健壮的网络应用。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com