news 2026/4/20 20:24:40

Linux网络编程之封装Socket模块现实意义和价值

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux网络编程之封装Socket模块现实意义和价值

第一阶段 设计架构Linux Socket模块

第一部分:规划 - 专注Linux Socket核心

1.1 精简架构设计

socket_lib/ ├── include/ # 对外头文件(只有1个!) │ └── socket.h # 统一对外接口 ├── src/ # 内部实现 │ ├── socket_core.c # 核心:创建、绑定、连接、关闭 │ ├── socket_io.c # 核心:发送、接收 │ ├── socket_opt.c # 核心:选项设置 │ └── socket_util.c # 辅助:地址转换、错误处理 └── examples/ ├── echo_server.c └── echo_client.c

1.2 核心功能树(只做最必要的)

Socket核心功能 ├── 1. 基础操作 │ ├── socket_create() # 创建Socket │ ├── socket_close() # 关闭Socket │ └── socket_dup() # 复制Socket ├── 2. 连接管理 │ ├── socket_bind() # 绑定地址 │ ├── socket_connect() # 连接远程 │ ├── socket_listen() # 开始监听 │ └── socket_accept() # 接受连接 ├── 3. 数据IO │ ├── socket_send() # 发送数据(TCP) │ ├── socket_recv() # 接收数据(TCP) │ ├── socket_sendto() # 发送到地址(UDP) │ └── socket_recvfrom() # 从地址接收(UDP) ├── 4. 选项设置 │ ├── socket_setopt() # 设置选项 │ ├── socket_getopt() # 获取选项 │ ├── socket_set_nonblock() # 非阻塞模式 │ └── socket_set_timeout() # 超时设置 └── 5. 工具函数 ├── socket_addr_create() # 创建地址 ├── socket_addr_str() # 地址转字符串 └── socket_error_str() # 错误描述

第二部分:简洁头文件设计

2.1 统一对外头文件 (socket.h)

/** * @file socket.h * @brief Linux Socket通信库 - 简洁版 * * 基于Linux系统调用的Socket封装,提供简单、直接、高效的接口。 * 使用基础库的common.h进行错误处理和线程安全。 * * @version 1.0 * @date 2024-01-01 */ ​ #ifndef SOCKET_H #define SOCKET_H ​ #include <stdbool.h> #include <stddef.h> #include <stdint.h> ​ #ifdef __cplusplus extern "C" { #endif ​ /* ==================== 基本类型 ==================== */ ​ /** * @brief Socket句柄(直接使用文件描述符) */ typedef int socket_t; ​ /** * @brief 无效Socket定义 */ #define SOCKET_INVALID (-1) ​ /** * @brief 地址族类型 */ typedef enum { SOCK_AF_INET = 0, /**< IPv4地址族 */ SOCK_AF_INET6, /**< IPv6地址族 */ SOCK_AF_UNIX /**< Unix域Socket */ } sock_family_t; ​ /** * @brief Socket类型 */ typedef enum { SOCK_TYPE_STREAM = 0, /**< 流式Socket (TCP) */ SOCK_TYPE_DGRAM /**< 数据报Socket (UDP) */ } sock_type_t; ​ /** * @brief 地址结构(足够简单) */ typedef struct { sock_family_t family; /**< 地址族 */ union { struct { uint32_t addr; /**< IPv4地址(网络字节序) */ uint16_t port; /**< 端口(网络字节序) */ } ipv4; struct { uint8_t addr[16]; /**< IPv6地址 */ uint16_t port; /**< 端口(网络字节序) */ } ipv6; struct { char path[108]; /**< Unix域路径 */ } unix; } u; } sock_addr_t; ​ /* ==================== 错误码 ==================== */ ​ /** * @brief 错误码(直接使用errno,简化) */ typedef int sock_error_t; ​ /** * @brief 成功码 */ #define SOCK_SUCCESS 0 ​ /* ==================== 初始化 ==================== */ ​ /** * @brief 初始化Socket库(可选) * * @return int 0成功,-1失败 */ int sock_init(void); ​ /** * @brief 清理Socket库 */ void sock_cleanup(void); ​ /* ==================== Socket生命周期 ==================== */ ​ /** * @brief 创建Socket * * @param family 地址族(SOCK_AF_INET/SOCK_AF_INET6/SOCK_AF_UNIX) * @param type Socket类型(SOCK_TYPE_STREAM/SOCK_TYPE_DGRAM) * @return socket_t Socket句柄,失败返回SOCKET_INVALID */ socket_t sock_create(sock_family_t family, sock_type_t type); ​ /** * @brief 关闭Socket * * @param sock Socket句柄 * @return int 0成功,-1失败 */ int sock_close(socket_t sock); ​ /** * @brief 复制Socket * * @param sock 源Socket * @return socket_t 新Socket,失败返回SOCKET_INVALID */ socket_t sock_dup(socket_t sock); ​ /* ==================== 连接管理 ==================== */ ​ /** * @brief 绑定地址 * * @param sock Socket句柄 * @param addr 地址 * @return int 0成功,-1失败 */ int sock_bind(socket_t sock, const sock_addr_t* addr); ​ /** * @brief 连接到远程 * * @param sock Socket句柄 * @param addr 远程地址 * @param timeout_ms 超时时间(毫秒,0表示阻塞) * @return int 0成功,-1失败 */ int sock_connect(socket_t sock, const sock_addr_t* addr, int timeout_ms); ​ /** * @brief 开始监听 * * @param sock Socket句柄 * @param backlog 等待连接队列长度 * @return int 0成功,-1失败 */ int sock_listen(socket_t sock, int backlog); ​ /** * @brief 接受连接 * * @param sock 监听Socket * @param client_addr 输出客户端地址(可为NULL) * @param timeout_ms 超时时间(毫秒,0表示阻塞) * @return socket_t 客户端Socket,失败返回SOCKET_INVALID */ socket_t sock_accept(socket_t sock, sock_addr_t* client_addr, int timeout_ms); ​ /* ==================== 数据IO ==================== */ ​ /** * @brief 发送数据(TCP) * * @param sock Socket句柄 * @param buf 数据缓冲区 * @param len 数据长度 * @param timeout_ms 超时时间(毫秒,0表示阻塞) * @return ssize_t 发送的字节数,-1失败 */ ssize_t sock_send(socket_t sock, const void* buf, size_t len, int timeout_ms); ​ /** * @brief 接收数据(TCP) * * @param sock Socket句柄 * @param buf 接收缓冲区 * @param len 缓冲区长度 * @param timeout_ms 超时时间(毫秒,0表示阻塞) * @return ssize_t 接收的字节数,-1失败,0连接关闭 */ ssize_t sock_recv(socket_t sock, void* buf, size_t len, int timeout_ms); ​ /** * @brief 发送数据到地址(UDP) * * @param sock Socket句柄 * @param buf 数据缓冲区 * @param len 数据长度 * @param addr 目标地址 * @return ssize_t 发送的字节数,-1失败 */ ssize_t sock_sendto(socket_t sock, const void* buf, size_t len, const sock_addr_t* addr); ​ /** * @brief 从地址接收数据(UDP) * * @param sock Socket句柄 * @param buf 接收缓冲区 * @param len 缓冲区长度 * @param src_addr 输出源地址(可为NULL) * @param timeout_ms 超时时间(毫秒,0表示阻塞) * @return ssize_t 接收的字节数,-1失败 */ ssize_t sock_recvfrom(socket_t sock, void* buf, size_t len, sock_addr_t* src_addr, int timeout_ms); ​ /* ==================== 选项设置 ==================== */ ​ /** * @brief 设置Socket选项 * * @param sock Socket句柄 * @param level 选项级别 * @param optname 选项名 * @param optval 选项值 * @param optlen 选项值长度 * @return int 0成功,-1失败 */ int sock_setopt(socket_t sock, int level, int optname, const void* optval, socklen_t optlen); ​ /** * @brief 获取Socket选项 * * @param sock Socket句柄 * @param level 选项级别 * @param optname 选项名 * @param optval 输出选项值 * @param optlen 输入输出选项值长度 * @return int 0成功,-1失败 */ int sock_getopt(socket_t sock, int level, int optname, void* optval, socklen_t* optlen); ​ /** * @brief 设置非阻塞模式 * * @param sock Socket句柄 * @param nonblock true非阻塞,false阻塞 * @return int 0成功,-1失败 */ int sock_set_nonblock(socket_t sock, bool nonblock); ​ /** * @brief 设置超时 * * @param sock Socket句柄 * @param send_timeout_ms 发送超时(毫秒) * @param recv_timeout_ms 接收超时(毫秒) * @return int 0成功,-1失败 */ int sock_set_timeout(socket_t sock, int send_timeout_ms, int recv_timeout_ms); ​ /* ==================== 地址处理 ==================== */ ​ /** * @brief 创建IPv4地址 * * @param ip IPv4地址字符串 * @param port 端口 * @param addr 输出地址 * @return int 0成功,-1失败 */ int sock_addr_ipv4(const char* ip, uint16_t port, sock_addr_t* addr); ​ /** * @brief 创建IPv6地址 * * @param ip IPv6地址字符串 * @param port 端口 * @param addr 输出地址 * @return int 0成功,-1失败 */ int sock_addr_ipv6(const char* ip, uint16_t port, sock_addr_t* addr); ​ /** * @brief 创建Unix域地址 * * @param path 文件路径 * @param addr 输出地址 * @return int 0成功,-1失败 */ int sock_addr_unix(const char* path, sock_addr_t* addr); ​ /** * @brief 地址转字符串 * * @param addr 地址 * @param buf 输出缓冲区 * @param len 缓冲区长度 * @return int 0成功,-1失败 */ int sock_addr_str(const sock_addr_t* addr, char* buf, size_t len); ​ /* ==================== 工具函数 ==================== */ ​ /** * @brief 获取错误描述 * * @param err 错误码 * @return const char* 错误描述 */ const char* sock_strerror(int err); ​ /** * @brief 检查Socket是否有效 * * @param sock Socket句柄 * @return true 有效 * @return false 无效 */ bool sock_valid(socket_t sock); ​ /** * @brief 获取Socket文件描述符(直接返回) * * @param sock Socket句柄 * @return int 文件描述符 */ static inline int sock_fd(socket_t sock) { return sock; } ​ #ifdef __cplusplus } #endif ​ #endif /* SOCKET_H */

第三部分:实现文件规划

3.1 socket_core.c - 核心功能实现

主要函数:

  • sock_create()- 创建Socket

  • sock_close()- 关闭Socket

  • sock_bind()- 绑定地址

  • sock_connect()- 连接远程

  • sock_listen()- 开始监听

  • sock_accept()- 接受连接

3.2 socket_io.c - 数据IO实现

主要函数:

  • sock_send()- TCP发送

  • sock_recv()- TCP接收

  • sock_sendto()- UDP发送

  • sock_recvfrom()- UDP接收

3.3 socket_opt.c - 选项设置实现

主要函数:

  • sock_setopt()- 设置选项

  • sock_getopt()- 获取选项

  • sock_set_nonblock()- 设置非阻塞

  • sock_set_timeout()- 设置超时

3.4 socket_util.c - 工具函数实现

主要函数:

  • sock_addr_ipv4()- 创建IPv4地址

  • sock_addr_ipv6()- 创建IPv6地址

  • sock_addr_unix()- 创建Unix地址

  • sock_addr_str()- 地址转字符串

  • sock_strerror()- 错误描述

第四部分:设计原则总结

  1. 极简主义:只做最必要的功能

  2. 直接映射:Socket句柄就是文件描述符

  3. 零封装:不隐藏系统调用,只是简化使用

  4. 无状态:不需要全局初始化(可选)

  5. 线程安全:依赖基础库的线程安全

  6. 错误透明:直接传递errno

这样的设计:

  • 学习成本低

  • 调试容易

  • 性能接近原生

  • 兼容性好

  • 维护简单

第二阶段 代码实现

第一部分:socket_core.c 实现

/** * @file socket_core.c * @brief Socket核心功能实现 * * 实现Socket的创建、绑定、连接、监听和接受连接等核心功能。 * 基于Linux系统调用,使用基础库进行错误处理和线程安全。 * * @version 1.0 * @date 2024-01-01 * @copyright Copyright (c) 2024 */ ​ #include "socket.h" #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <sys/un.h> #include <netinet/in.h> #include <arpa/inet.h> #include <fcntl.h> #include <errno.h> #include <poll.h> ​ /* ==================== 内部辅助函数 ==================== */ ​ /** * @brief 将地址族转换为系统AF值 * * @param family 地址族 * @return int 系统AF值,失败返回-1 */ static int family_to_af(sock_family_t family) { switch (family) { case SOCK_AF_INET: return AF_INET; case SOCK_AF_INET6: return AF_INET6; case SOCK_AF_UNIX: return AF_UNIX; default: return -1; } } ​ /** * @brief 将Socket类型转换为系统SOCK值 * * @param type Socket类型 * @return int 系统SOCK值,失败返回-1 */ static int type_to_sock(sock_type_t type) { switch (type) { case SOCK_TYPE_STREAM: return SOCK_STREAM; case SOCK_TYPE_DGRAM: return SOCK_DGRAM; default: return -1; } } ​ /** * @brief 获取协议号 * * @param type Socket类型 * @return int 协议号,0表示默认 */ static int get_protocol(sock_type_t type) { switch (type) { case SOCK_TYPE_STREAM: return IPPROTO_TCP; case SOCK_TYPE_DGRAM: return IPPROTO_UDP; default: return 0; } } ​ /** * @brief 将地址结构转换为系统地址结构 * * @param addr 地址结构 * @param sys_addr 输出系统地址结构 * @param addrlen 输出地址长度 * @return int 0成功,-1失败 */ static int addr_to_sys(const sock_addr_t* addr, struct sockaddr* sys_addr, socklen_t* addrlen) { if (!addr || !sys_addr || !addrlen) { errno = EINVAL; return -1; } switch (addr->family) { case SOCK_AF_INET: { struct sockaddr_in* in_addr = (struct sockaddr_in*)sys_addr; memset(in_addr, 0, sizeof(*in_addr)); in_addr->sin_family = AF_INET; in_addr->sin_addr.s_addr = addr->u.ipv4.addr; in_addr->sin_port = addr->u.ipv4.port; *addrlen = sizeof(*in_addr); return 0; } case SOCK_AF_INET6: { struct sockaddr_in6* in6_addr = (struct sockaddr_in6*)sys_addr; memset(in6_addr, 0, sizeof(*in6_addr)); in6_addr->sin6_family = AF_INET6; memcpy(&in6_addr->sin6_addr, addr->u.ipv6.addr, 16); in6_addr->sin6_port = addr->u.ipv6.port; *addrlen = sizeof(*in6_addr); return 0; } case SOCK_AF_UNIX: { struct sockaddr_un* un_addr = (struct sockaddr_un*)sys_addr; memset(un_addr, 0, sizeof(*un_addr)); un_addr->sun_family = AF_UNIX; strncpy(un_addr->sun_path, addr->u.unix.path, sizeof(un_addr->sun_path) - 1); *addrlen = sizeof(*un_addr); return 0; } default: errno = EAFNOSUPPORT; return -1; } } ​ /** * @brief 将系统地址结构转换为地址结构 * * @param sys_addr 系统地址结构 * @param addrlen 地址长度 * @param addr 输出地址结构 * @return int 0成功,-1失败 */ static int addr_from_sys(const struct sockaddr* sys_addr, socklen_t addrlen, sock_addr_t* addr) { if (!sys_addr || !addr) { errno = EINVAL; return -1; } memset(addr, 0, sizeof(*addr)); switch (sys_addr->sa_family) { case AF_INET: { const struct sockaddr_in* in_addr = (const struct sockaddr_in*)sys_addr; addr->family = SOCK_AF_INET; addr->u.ipv4.addr = in_addr->sin_addr.s_addr; addr->u.ipv4.port = in_addr->sin_port; return 0; } case AF_INET6: { const struct sockaddr_in6* in6_addr = (const struct sockaddr_in6*)sys_addr; addr->family = SOCK_AF_INET6; memcpy(addr->u.ipv6.addr, &in6_addr->sin6_addr, 16); addr->u.ipv6.port = in6_addr->sin6_port; return 0; } case AF_UNIX: { const struct sockaddr_un* un_addr = (const struct sockaddr_un*)sys_addr; addr->family = SOCK_AF_UNIX; strncpy(addr->u.unix.path, un_addr->sun_path, sizeof(addr->u.unix.path) - 1); return 0; } default: errno = EAFNOSUPPORT; return -1; } } ​ /** * @brief 检查Socket有效性 * * @param sock Socket句柄 * @return int 0有效,-1无效 */ static int check_socket(socket_t sock) { if (sock == SOCKET_INVALID) { errno = EBADF; return -1; } int error = 0; socklen_t len = sizeof(error); if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { return -1; } if (error != 0) { errno = error; return -1; } return 0; } ​ /** * @brief 轮询等待事件 * * @param fd 文件描述符 * @param events 等待的事件(POLLIN/POLLOUT) * @param timeout_ms 超时时间(毫秒) * @return int >0有事件,0超时,-1错误 */ static int poll_wait(int fd, short events, int timeout_ms) { struct pollfd pfd; pfd.fd = fd; pfd.events = events; pfd.revents = 0; int ret = poll(&pfd, 1, timeout_ms); if (ret < 0) { return -1; } else if (ret == 0) { errno = ETIMEDOUT; return 0; } if (pfd.revents & (POLLERR | POLLHUP | POLLNVAL)) { errno = ECONNREFUSED; return -1; } return ret; } ​ /* ==================== 公开API实现 ==================== */ ​ /** * @brief 创建Socket * * @param family 地址族(SOCK_AF_INET/SOCK_AF_INET6/SOCK_AF_UNIX) * @param type Socket类型(SOCK_TYPE_STREAM/SOCK_TYPE_DGRAM) * @return socket_t Socket句柄,失败返回SOCKET_INVALID */ socket_t sock_create(sock_family_t family, sock_type_t type) { int af = family_to_af(family); int sock_type = type_to_sock(type); int protocol = get_protocol(type); if (af == -1 || sock_type == -1) { errno = EINVAL; return SOCKET_INVALID; } int fd = socket(af, sock_type, protocol); if (fd < 0) { return SOCKET_INVALID; } /* 设置一些默认选项 */ int reuse = 1; setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); /* 对于TCP,设置NODELAY减少延迟 */ if (type == SOCK_TYPE_STREAM) { int nodelay = 1; setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &nodelay, sizeof(nodelay)); } LOG_DEBUG("SOCKET", "Created socket fd=%d, family=%d, type=%d", fd, family, type); return fd; } ​ /** * @brief 关闭Socket * * @param sock Socket句柄 * @return int 0成功,-1失败 */ int sock_close(socket_t sock) { if (sock == SOCKET_INVALID) { errno = EBADF; return -1; } /* 先尝试正常关闭 */ if (shutdown(sock, SHUT_RDWR) < 0) { if (errno != ENOTCONN && errno != EINVAL) { LOG_WARN("SOCKET", "shutdown failed on fd=%d: %s", sock, strerror(errno)); } } /* 关闭文件描述符 */ if (close(sock) < 0) { LOG_ERROR("SOCKET", "close failed on fd=%d: %s", sock, strerror(errno)); return -1; } LOG_DEBUG("SOCKET", "Closed socket fd=%d", sock); return 0; } ​ /** * @brief 复制Socket * * @param sock 源Socket * @return socket_t 新Socket,失败返回SOCKET_INVALID */ socket_t sock_dup(socket_t sock) { if (sock == SOCKET_INVALID) { errno = EBADF; return SOCKET_INVALID; } int new_fd = dup(sock); if (new_fd < 0) { return SOCKET_INVALID; } LOG_DEBUG("SOCKET", "Duplicated socket fd=%d -> fd=%d", sock, new_fd); return new_fd; } ​ /** * @brief 绑定地址 * * @param sock Socket句柄 * @param addr 地址 * @return int 0成功,-1失败 */ int sock_bind(socket_t sock, const sock_addr_t* addr) { if (sock == SOCKET_INVALID || !addr) { errno = EINVAL; return -1; } struct sockaddr_storage sys_addr; socklen_t addrlen; if (addr_to_sys(addr, (struct sockaddr*)&sys_addr, &addrlen) < 0) { return -1; } /* Unix域Socket需要删除已存在的文件 */ if (addr->family == SOCK_AF_UNIX) { unlink(addr->u.unix.path); } if (bind(sock, (struct sockaddr*)&sys_addr, addrlen) < 0) { LOG_ERROR("SOCKET", "bind failed on fd=%d: %s", sock, strerror(errno)); return -1; } LOG_DEBUG("SOCKET", "Bound socket fd=%d to address", sock); return 0; } ​ /** * @brief 连接到远程 * * @param sock Socket句柄 * @param addr 远程地址 * @param timeout_ms 超时时间(毫秒,0表示阻塞) * @return int 0成功,-1失败 */ int sock_connect(socket_t sock, const sock_addr_t* addr, int timeout_ms) { if (sock == SOCKET_INVALID || !addr) { errno = EINVAL; return -1; } /* 检查Socket状态 */ if (check_socket(sock) < 0) { return -1; } struct sockaddr_storage sys_addr; socklen_t addrlen; if (addr_to_sys(addr, (struct sockaddr*)&sys_addr, &addrlen) < 0) { return -1; } /* 如果需要超时,先设置为非阻塞 */ int flags = 0; int need_restore = 0; if (timeout_ms > 0) { flags = fcntl(sock, F_GETFL, 0); if (flags < 0) { return -1; } if (!(flags & O_NONBLOCK)) { if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) { return -1; } need_restore = 1; } } /* 尝试连接 */ int ret = connect(sock, (struct sockaddr*)&sys_addr, addrlen); if (ret < 0) { if (errno == EINPROGRESS) { /* 非阻塞连接,等待完成 */ if (timeout_ms > 0) { ret = poll_wait(sock, POLLOUT, timeout_ms); if (ret <= 0) { if (need_restore) { fcntl(sock, F_SETFL, flags); } return ret; } /* 检查连接是否成功 */ int error = 0; socklen_t len = sizeof(error); if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { if (need_restore) { fcntl(sock, F_SETFL, flags); } return -1; } if (error != 0) { errno = error; if (need_restore) { fcntl(sock, F_SETFL, flags); } return -1; } } else { /* 没有超时,连接会阻塞直到完成 */ if (need_restore) { fcntl(sock, F_SETFL, flags); } errno = EINPROGRESS; return -1; } } else { /* 其他连接错误 */ if (need_restore) { fcntl(sock, F_SETFL, flags); } LOG_ERROR("SOCKET", "connect failed on fd=%d: %s", sock, strerror(errno)); return -1; } } /* 恢复原来的标志 */ if (need_restore) { fcntl(sock, F_SETFL, flags); } LOG_DEBUG("SOCKET", "Connected socket fd=%d", sock); return 0; } ​ /** * @brief 开始监听 * * @param sock Socket句柄 * @param backlog 等待连接队列长度 * @return int 0成功,-1失败 */ int sock_listen(socket_t sock, int backlog) { if (sock == SOCKET_INVALID) { errno = EINVAL; return -1; } if (backlog <= 0) { backlog = 128; /* 默认值 */ } if (listen(sock, backlog) < 0) { LOG_ERROR("SOCKET", "listen failed on fd=%d: %s", sock, strerror(errno)); return -1; } LOG_DEBUG("SOCKET", "Listening on socket fd=%d, backlog=%d", sock, backlog); return 0; } ​ /** * @brief 接受连接 * * @param sock 监听Socket * @param client_addr 输出客户端地址(可为NULL) * @param timeout_ms 超时时间(毫秒,0表示阻塞) * @return socket_t 客户端Socket,失败返回SOCKET_INVALID */ socket_t sock_accept(socket_t sock, sock_addr_t* client_addr, int timeout_ms) { if (sock == SOCKET_INVALID) { errno = EINVAL; return SOCKET_INVALID; } /* 如果需要超时,先轮询等待 */ if (timeout_ms > 0) { int ret = poll_wait(sock, POLLIN, timeout_ms); if (ret <= 0) { return SOCKET_INVALID; } } struct sockaddr_storage addr; socklen_t addrlen = sizeof(addr); int client_fd = accept(sock, (struct sockaddr*)&addr, &addrlen); if (client_fd < 0) { LOG_ERROR("SOCKET", "accept failed on fd=%d: %s", sock, strerror(errno)); return SOCKET_INVALID; } /* 转换客户端地址 */ if (client_addr) { if (addr_from_sys((struct sockaddr*)&addr, addrlen, client_addr) < 0) { LOG_WARN("SOCKET", "Failed to convert client address"); } } /* 设置一些选项 */ int reuse = 1; setsockopt(client_fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); LOG_DEBUG("SOCKET", "Accepted connection fd=%d from listening fd=%d", client_fd, sock); return client_fd; } ​ /** * @brief 初始化Socket库(可选) * * @return int 0成功,-1失败 */ int sock_init(void) { /* Linux下不需要特殊的初始化 */ LOG_INFO("SOCKET", "Socket library initialized"); return 0; } ​ /** * @brief 清理Socket库 */ void sock_cleanup(void) { /* Linux下不需要特殊的清理 */ LOG_INFO("SOCKET", "Socket library cleaned up"); } ​ /** * @brief 检查Socket是否有效 * * @param sock Socket句柄 * @return true 有效 * @return false 无效 */ bool sock_valid(socket_t sock) { return (sock != SOCKET_INVALID && check_socket(sock) == 0); }

第二部分:socket_io.c 实现

/** * @file socket_io.c * @brief Socket数据IO实现 * * 实现Socket的数据发送和接收功能,支持TCP和UDP。 * 提供超时控制和错误处理。 * * @version 1.0 * @date 2024-01-01 * @copyright Copyright (c) 2024 */ ​ #include "socket.h" #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <sys/time.h> #include <errno.h> #include <poll.h> ​ /* ==================== 内部辅助函数 ==================== */ ​ /** * @brief 设置Socket超时 * * @param sock Socket句柄 * @param send_timeout_ms 发送超时(毫秒) * @param recv_timeout_ms 接收超时(毫秒) * @return int 0成功,-1失败 */ static int set_socket_timeout(socket_t sock, int send_timeout_ms, int recv_timeout_ms) { struct timeval tv; /* 设置发送超时 */ if (send_timeout_ms >= 0) { tv.tv_sec = send_timeout_ms / 1000; tv.tv_usec = (send_timeout_ms % 1000) * 1000; if (setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0) { return -1; } } /* 设置接收超时 */ if (recv_timeout_ms >= 0) { tv.tv_sec = recv_timeout_ms / 1000; tv.tv_usec = (recv_timeout_ms % 1000) * 1000; if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) { return -1; } } return 0; } ​ /** * @brief 等待Socket可读或可写 * * @param sock Socket句柄 * @param for_read true等待可读,false等待可写 * @param timeout_ms 超时时间(毫秒) * @return int >0可操作,0超时,-1错误 */ static int wait_socket(socket_t sock, bool for_read, int timeout_ms) { short events = for_read ? POLLIN : POLLOUT; return poll_wait(sock, events, timeout_ms); } ​ /** * @brief 安全的发送数据(处理EINTR) * * @param sock Socket句柄 * @param buf 数据缓冲区 * @param len 数据长度 * @param flags 发送标志 * @return ssize_t 发送的字节数,-1失败 */ static ssize_t safe_send(socket_t sock, const void* buf, size_t len, int flags) { ssize_t sent; do { sent = send(sock, buf, len, flags); } while (sent < 0 && errno == EINTR); return sent; } ​ /** * @brief 安全的接收数据(处理EINTR) * * @param sock Socket句柄 * @param buf 接收缓冲区 * @param len 缓冲区长度 * @param flags 接收标志 * @return ssize_t 接收的字节数,-1失败,0连接关闭 */ static ssize_t safe_recv(socket_t sock, void* buf, size_t len, int flags) { ssize_t received; do { received = recv(sock, buf, len, flags); } while (received < 0 && errno == EINTR); return received; } ​ /** * @brief 安全的发送到地址(处理EINTR) * * @param sock Socket句柄 * @param buf 数据缓冲区 * @param len 数据长度 * @param flags 发送标志 * @param addr 目标地址 * @param addrlen 地址长度 * @return ssize_t 发送的字节数,-1失败 */ static ssize_t safe_sendto(socket_t sock, const void* buf, size_t len, int flags, const struct sockaddr* addr, socklen_t addrlen) { ssize_t sent; do { sent = sendto(sock, buf, len, flags, addr, addrlen); } while (sent < 0 && errno == EINTR); return sent; } ​ /** * @brief 安全的从地址接收(处理EINTR) * * @param sock Socket句柄 * @param buf 接收缓冲区 * @param len 缓冲区长度 * @param flags 接收标志 * @param addr 输出源地址 * @param addrlen 输入输出地址长度 * @return ssize_t 接收的字节数,-1失败 */ static ssize_t safe_recvfrom(socket_t sock, void* buf, size_t len, int flags, struct sockaddr* addr, socklen_t* addrlen) { ssize_t received; do { received = recvfrom(sock, buf, len, flags, addr, addrlen); } while (received < 0 && errno == EINTR); return received; } ​ /* ==================== 公开API实现 ==================== */ ​ /** * @brief 发送数据(TCP) * * @param sock Socket句柄 * @param buf 数据缓冲区 * @param len 数据长度 * @param timeout_ms 超时时间(毫秒,0表示阻塞) * @return ssize_t 发送的字节数,-1失败 */ ssize_t sock_send(socket_t sock, const void* buf, size_t len, int timeout_ms) { if (sock == SOCKET_INVALID || !buf || len == 0) { errno = EINVAL; return -1; } /* 检查Socket状态 */ if (check_socket(sock) < 0) { return -1; } /* 设置临时超时 */ int saved_send_timeout = -1; socklen_t optlen = sizeof(saved_send_timeout); if (timeout_ms > 0) { /* 保存原来的发送超时 */ getsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &saved_send_timeout, &optlen); /* 设置新的发送超时 */ if (set_socket_timeout(sock, timeout_ms, -1) < 0) { return -1; } } /* 发送数据 */ ssize_t sent = safe_send(sock, buf, len, 0); /* 恢复原来的超时设置 */ if (timeout_ms > 0 && saved_send_timeout >= 0) { struct timeval tv; tv.tv_sec = saved_send_timeout / 1000000; tv.tv_usec = saved_send_timeout % 1000000; setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); } if (sent < 0) { if (errno == EAGAIN || errno == EWOULDBLOCK) { errno = ETIMEDOUT; } LOG_DEBUG("SOCKET", "send failed on fd=%d: %s", sock, strerror(errno)); return -1; } LOG_DEBUG("SOCKET", "Sent %zd bytes on fd=%d", sent, sock); return sent; } ​ /** * @brief 接收数据(TCP) * * @param sock Socket句柄 * @param buf 接收缓冲区 * @param len 缓冲区长度 * @param timeout_ms 超时时间(毫秒,0表示阻塞) * @return ssize_t 接收的字节数,-1失败,0连接关闭 */ ssize_t sock_recv(socket_t sock, void* buf, size_t len, int timeout_ms) { if (sock == SOCKET_INVALID || !buf || len == 0) { errno = EINVAL; return -1; } /* 检查Socket状态 */ if (check_socket(sock) < 0) { return -1; } /* 如果需要超时,先等待数据可读 */ if (timeout_ms > 0) { int ret = wait_socket(sock, true, timeout_ms); if (ret <= 0) { return ret; /* 0表示超时,-1表示错误 */ } } /* 接收数据 */ ssize_t received = safe_recv(sock, buf, len, 0); if (received < 0) { if (errno == EAGAIN || errno == EWOULDBLOCK) { errno = ETIMEDOUT; } LOG_DEBUG("SOCKET", "recv failed on fd=%d: %s", sock, strerror(errno)); return -1; } else if (received == 0) { LOG_DEBUG("SOCKET", "Connection closed on fd=%d", sock); return 0; } LOG_DEBUG("SOCKET", "Received %zd bytes on fd=%d", received, sock); return received; } ​ /** * @brief 发送数据到地址(UDP) * * @param sock Socket句柄 * @param buf 数据缓冲区 * @param len 数据长度 * @param addr 目标地址 * @return ssize_t 发送的字节数,-1失败 */ ssize_t sock_sendto(socket_t sock, const void* buf, size_t len, const sock_addr_t* addr) { if (sock == SOCKET_INVALID || !buf || len == 0 || !addr) { errno = EINVAL; return -1; } struct sockaddr_storage sys_addr; socklen_t addrlen; if (addr_to_sys(addr, (struct sockaddr*)&sys_addr, &addrlen) < 0) { return -1; } /* 发送数据 */ ssize_t sent = safe_sendto(sock, buf, len, 0, (struct sockaddr*)&sys_addr, addrlen); if (sent < 0) { if (errno == EMSGSIZE) { LOG_WARN("SOCKET", "Message too large on fd=%d", sock); } else { LOG_DEBUG("SOCKET", "sendto failed on fd=%d: %s", sock, strerror(errno)); } return -1; } LOG_DEBUG("SOCKET", "Sent %zd bytes via UDP on fd=%d", sent, sock); return sent; } ​ /** * @brief 从地址接收数据(UDP) * * @param sock Socket句柄 * @param buf 接收缓冲区 * @param len 缓冲区长度 * @param src_addr 输出源地址(可为NULL) * @param timeout_ms 超时时间(毫秒,0表示阻塞) * @return ssize_t 接收的字节数,-1失败 */ ssize_t sock_recvfrom(socket_t sock, void* buf, size_t len, sock_addr_t* src_addr, int timeout_ms) { if (sock == SOCKET_INVALID || !buf || len == 0) { errno = EINVAL; return -1; } /* 如果需要超时,先等待数据可读 */ if (timeout_ms > 0) { int ret = wait_socket(sock, true, timeout_ms); if (ret <= 0) { return ret; /* 0表示超时,-1表示错误 */ } } struct sockaddr_storage addr; socklen_t addrlen = sizeof(addr); /* 接收数据 */ ssize_t received = safe_recvfrom(sock, buf, len, 0, (struct sockaddr*)&addr, &addrlen); if (received < 0) { if (errno == EAGAIN || errno == EWOULDBLOCK) { errno = ETIMEDOUT; } LOG_DEBUG("SOCKET", "recvfrom failed on fd=%d: %s", sock, strerror(errno)); return -1; } /* 转换源地址 */ if (src_addr && received > 0) { if (addr_from_sys((struct sockaddr*)&addr, addrlen, src_addr) < 0) { LOG_WARN("SOCKET", "Failed to convert source address"); } } LOG_DEBUG("SOCKET", "Received %zd bytes via UDP on fd=%d", received, sock); return received; }

第三部分:socket_opt.c 实现

/** * @file socket_opt.c * @brief Socket选项设置实现 * * 实现Socket选项的设置和获取功能,包括非阻塞模式、超时设置等。 * 提供常用选项的便捷接口。 * * @version 1.0 * @date 2024-01-01 * @copyright Copyright (c) 2024 */ ​ #include "socket.h" #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <netinet/tcp.h> #include <fcntl.h> #include <errno.h> ​ /* ==================== 内部辅助函数 ==================== */ ​ /** * @brief 检查Socket句柄有效性 * * @param sock Socket句柄 * @return int 0有效,-1无效 */ static int validate_socket(socket_t sock) { if (sock == SOCKET_INVALID) { errno = EBADF; return -1; } /* 使用fstat检查文件描述符是否有效 */ struct stat st; if (fstat(sock, &st) < 0) { return -1; } return 0; } ​ /** * @brief 获取当前Socket标志 * * @param sock Socket句柄 * @param flags 输出标志 * @return int 0成功,-1失败 */ static int get_socket_flags(socket_t sock, int* flags) { if (!flags) { errno = EINVAL; return -1; } *flags = fcntl(sock, F_GETFL, 0); if (*flags < 0) { return -1; } return 0; } ​ /** * @brief 设置Socket标志 * * @param sock Socket句柄 * @param flags 新标志 * @return int 0成功,-1失败 */ static int set_socket_flags(socket_t sock, int flags) { if (fcntl(sock, F_SETFL, flags) < 0) { return -1; } return 0; } ​ /** * @brief 设置超时值 * * @param sock Socket句柄 * @param level 选项级别 * @param optname 选项名称 * @param timeout_ms 超时时间(毫秒) * @return int 0成功,-1失败 */ static int set_timeout_option(socket_t sock, int level, int optname, int timeout_ms) { struct timeval tv; if (timeout_ms < 0) { /* 不修改超时设置 */ return 0; } tv.tv_sec = timeout_ms / 1000; tv.tv_usec = (timeout_ms % 1000) * 1000; if (setsockopt(sock, level, optname, &tv, sizeof(tv)) < 0) { return -1; } return 0; } ​ /** * @brief 获取超时值 * * @param sock Socket句柄 * @param level 选项级别 * @param optname 选项名称 * @param timeout_ms 输出超时时间(毫秒) * @return int 0成功,-1失败 */ static int get_timeout_option(socket_t sock, int level, int optname, int* timeout_ms) { struct timeval tv; socklen_t len = sizeof(tv); if (!timeout_ms) { errno = EINVAL; return -1; } if (getsockopt(sock, level, optname, &tv, &len) < 0) { return -1; } *timeout_ms = (int)(tv.tv_sec * 1000 + tv.tv_usec / 1000); return 0; } ​ /* ==================== 公开API实现 ==================== */ ​ /** * @brief 设置Socket选项 * * @param sock Socket句柄 * @param level 选项级别 * @param optname 选项名 * @param optval 选项值 * @param optlen 选项值长度 * @return int 0成功,-1失败 */ int sock_setopt(socket_t sock, int level, int optname, const void* optval, socklen_t optlen) { if (sock == SOCKET_INVALID || !optval || optlen == 0) { errno = EINVAL; return -1; } if (validate_socket(sock) < 0) { return -1; } if (setsockopt(sock, level, optname, optval, optlen) < 0) { LOG_DEBUG("SOCKET", "setsockopt failed on fd=%d: %s", sock, strerror(errno)); return -1; } LOG_DEBUG("SOCKET", "Set option fd=%d, level=%d, optname=%d", sock, level, optname); return 0; } ​ /** * @brief 获取Socket选项 * * @param sock Socket句柄 * @param level 选项级别 * @param optname 选项名 * @param optval 输出选项值 * @param optlen 输入输出选项值长度 * @return int 0成功,-1失败 */ int sock_getopt(socket_t sock, int level, int optname, void* optval, socklen_t* optlen) { if (sock == SOCKET_INVALID || !optval || !optlen || *optlen == 0) { errno = EINVAL; return -1; } if (validate_socket(sock) < 0) { return -1; } if (getsockopt(sock, level, optname, optval, optlen) < 0) { LOG_DEBUG("SOCKET", "getsockopt failed on fd=%d: %s", sock, strerror(errno)); return -1; } LOG_DEBUG("SOCKET", "Got option fd=%d, level=%d, optname=%d", sock, level, optname); return 0; } ​ /** * @brief 设置非阻塞模式 * * @param sock Socket句柄 * @param nonblock true非阻塞,false阻塞 * @return int 0成功,-1失败 */ int sock_set_nonblock(socket_t sock, bool nonblock) { if (sock == SOCKET_INVALID) { errno = EINVAL; return -1; } if (validate_socket(sock) < 0) { return -1; } int flags; if (get_socket_flags(sock, &flags) < 0) { return -1; } if (nonblock) { flags |= O_NONBLOCK; } else { flags &= ~O_NONBLOCK; } if (set_socket_flags(sock, flags) < 0) { return -1; } LOG_DEBUG("SOCKET", "Set nonblock=%s on fd=%d", nonblock ? "true" : "false", sock); return 0; } ​ /** * @brief 设置超时 * * @param sock Socket句柄 * @param send_timeout_ms 发送超时(毫秒) * @param recv_timeout_ms 接收超时(毫秒) * @return int 0成功,-1失败 */ int sock_set_timeout(socket_t sock, int send_timeout_ms, int recv_timeout_ms) { if (sock == SOCKET_INVALID) { errno = EINVAL; return -1; } if (validate_socket(sock) < 0) { return -1; } /* 设置发送超时 */ if (set_timeout_option(sock, SOL_SOCKET, SO_SNDTIMEO, send_timeout_ms) < 0) { LOG_WARN("SOCKET", "Failed to set send timeout on fd=%d: %s", sock, strerror(errno)); } /* 设置接收超时 */ if (set_timeout_option(sock, SOL_SOCKET, SO_RCVTIMEO, recv_timeout_ms) < 0) { LOG_WARN("SOCKET", "Failed to set recv timeout on fd=%d: %s", sock, strerror(errno)); } LOG_DEBUG("SOCKET", "Set timeout on fd=%d: send=%dms, recv=%dms", sock, send_timeout_ms, recv_timeout_ms); return 0; } ​ /** * @brief 获取发送超时 * * @param sock Socket句柄 * @param timeout_ms 输出发送超时(毫秒) * @return int 0成功,-1失败 */ int sock_get_send_timeout(socket_t sock, int* timeout_ms) { if (sock == SOCKET_INVALID || !timeout_ms) { errno = EINVAL; return -1; } return get_timeout_option(sock, SOL_SOCKET, SO_SNDTIMEO, timeout_ms); } ​ /** * @brief 获取接收超时 * * @param sock Socket句柄 * @param timeout_ms 输出接收超时(毫秒) * @return int 0成功,-1失败 */ int sock_get_recv_timeout(socket_t sock, int* timeout_ms) { if (sock == SOCKET_INVALID || !timeout_ms) { errno = EINVAL; return -1; } return get_timeout_option(sock, SOL_SOCKET, SO_RCVTIMEO, timeout_ms); } ​ /** * @brief 设置地址重用选项 * * @param sock Socket句柄 * @param reuse 是否启用地址重用 * @return int 0成功,-1失败 */ int sock_set_reuseaddr(socket_t sock, bool reuse) { if (sock == SOCKET_INVALID) { errno = EINVAL; return -1; } int optval = reuse ? 1 : 0; if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0) { LOG_DEBUG("SOCKET", "Failed to set SO_REUSEADDR on fd=%d: %s", sock, strerror(errno)); return -1; } LOG_DEBUG("SOCKET", "Set SO_REUSEADDR=%d on fd=%d", optval, sock); return 0; } ​ /** * @brief 设置端口重用选项 * * @param sock Socket句柄 * @param reuse 是否启用端口重用 * @return int 0成功,-1失败 */ int sock_set_reuseport(socket_t sock, bool reuse) { if (sock == SOCKET_INVALID) { errno = EINVAL; return -1; } int optval = reuse ? 1 : 0; #ifdef SO_REUSEPORT if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)) < 0) { LOG_DEBUG("SOCKET", "Failed to set SO_REUSEPORT on fd=%d: %s", sock, strerror(errno)); return -1; } LOG_DEBUG("SOCKET", "Set SO_REUSEPORT=%d on fd=%d", optval, sock); return 0; #else LOG_WARN("SOCKET", "SO_REUSEPORT not supported on this system"); errno = ENOPROTOOPT; return -1; #endif } ​ /** * @brief 设置TCP NODELAY选项 * * @param sock Socket句柄 * @param nodelay 是否禁用Nagle算法 * @return int 0成功,-1失败 */ int sock_set_tcp_nodelay(socket_t sock, bool nodelay) { if (sock == SOCKET_INVALID) { errno = EINVAL; return -1; } int optval = nodelay ? 1 : 0; if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval)) < 0) { LOG_DEBUG("SOCKET", "Failed to set TCP_NODELAY on fd=%d: %s", sock, strerror(errno)); return -1; } LOG_DEBUG("SOCKET", "Set TCP_NODELAY=%d on fd=%d", optval, sock); return 0; } ​ /** * @brief 设置保持连接选项 * * @param sock Socket句柄 * @param keepalive 是否启用保持连接 * @return int 0成功,-1失败 */ int sock_set_keepalive(socket_t sock, bool keepalive) { if (sock == SOCKET_INVALID) { errno = EINVAL; return -1; } int optval = keepalive ? 1 : 0; if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval)) < 0) { LOG_DEBUG("SOCKET", "Failed to set SO_KEEPALIVE on fd=%d: %s", sock, strerror(errno)); return -1; } LOG_DEBUG("SOCKET", "Set SO_KEEPALIVE=%d on fd=%d", optval, sock); return 0; } ​ /** * @brief 设置发送缓冲区大小 * * @param sock Socket句柄 * @param size 缓冲区大小(字节) * @return int 0成功,-1失败 */ int sock_set_send_buffer(socket_t sock, int size) { if (sock == SOCKET_INVALID || size <= 0) { errno = EINVAL; return -1; } if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)) < 0) { LOG_DEBUG("SOCKET", "Failed to set SO_SNDBUF on fd=%d: %s", sock, strerror(errno)); return -1; } LOG_DEBUG("SOCKET", "Set SO_SNDBUF=%d on fd=%d", size, sock); return 0; } ​ /** * @brief 设置接收缓冲区大小 * * @param sock Socket句柄 * @param size 缓冲区大小(字节) * @return int 0成功,-1失败 */ int sock_set_recv_buffer(socket_t sock, int size) { if (sock == SOCKET_INVALID || size <= 0) { errno = EINVAL; return -1; } if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)) < 0) { LOG_DEBUG("SOCKET", "Failed to set SO_RCVBUF on fd=%d: %s", sock, strerror(errno)); return -1; } LOG_DEBUG("SOCKET", "Set SO_RCVBUF=%d on fd=%d", size, sock); return 0; } ​ /** * @brief 设置Linger选项 * * @param sock Socket句柄 * @param enable 是否启用Linger * @param timeout_sec 超时时间(秒) * @return int 0成功,-1失败 */ int sock_set_linger(socket_t sock, bool enable, int timeout_sec) { if (sock == SOCKET_INVALID) { errno = EINVAL; return -1; } struct linger l; l.l_onoff = enable ? 1 : 0; l.l_linger = timeout_sec; if (setsockopt(sock, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) < 0) { LOG_DEBUG("SOCKET", "Failed to set SO_LINGER on fd=%d: %s", sock, strerror(errno)); return -1; } LOG_DEBUG("SOCKET", "Set SO_LINGER: onoff=%d, linger=%d on fd=%d", l.l_onoff, l.l_linger, sock); return 0; } ​ /** * @brief 获取Socket错误状态 * * @param sock Socket句柄 * @param error 输出错误码 * @return int 0成功,-1失败 */ int sock_get_error(socket_t sock, int* error) { if (sock == SOCKET_INVALID || !error) { errno = EINVAL; return -1; } socklen_t len = sizeof(*error); if (getsockopt(sock, SOL_SOCKET, SO_ERROR, error, &len) < 0) { return -1; } return 0; }

第四部分:socket_util.c 实现

/** * @file socket_util.c * @brief Socket工具函数实现 * * 实现地址处理、错误处理等工具函数。 * 提供便捷的地址创建和转换功能。 * * @version 1.0 * @date 2024-01-01 * @copyright Copyright (c) 2024 */ ​ #include "socket.h" #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include <errno.h> ​ /* ==================== 内部辅助函数 ==================== */ ​ /** * @brief 解析主机名 * * @param host 主机名或IP地址 * @param addr 输出IPv4地址 * @return int 0成功,-1失败 */ static int resolve_hostname(const char* host, struct in_addr* addr) { if (!host || !addr) { return -1; } /* 首先尝试直接解析为IP地址 */ if (inet_pton(AF_INET, host, addr) == 1) { return 0; } /* 如果失败,尝试通过DNS解析 */ struct addrinfo hints, *result = NULL; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; int ret = getaddrinfo(host, NULL, &hints, &result); if (ret != 0) { return -1; } /* 获取第一个IPv4地址 */ struct addrinfo* p; for (p = result; p != NULL; p = p->ai_next) { if (p->ai_family == AF_INET) { struct sockaddr_in* sin = (struct sockaddr_in*)p->ai_addr; *addr = sin->sin_addr; freeaddrinfo(result); return 0; } } freeaddrinfo(result); return -1; } ​ /** * @brief 解析IPv6主机名 * * @param host 主机名或IPv6地址 * @param addr 输出IPv6地址 * @return int 0成功,-1失败 */ static int resolve_hostname_ipv6(const char* host, struct in6_addr* addr) { if (!host || !addr) { return -1; } /* 首先尝试直接解析为IPv6地址 */ if (inet_pton(AF_INET6, host, addr) == 1) { return 0; } /* 如果失败,尝试通过DNS解析 */ struct addrinfo hints, *result = NULL; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET6; hints.ai_socktype = SOCK_STREAM; int ret = getaddrinfo(host, NULL, &hints, &result); if (ret != 0) { return -1; } /* 获取第一个IPv6地址 */ struct addrinfo* p; for (p = result; p != NULL; p = p->ai_next) { if (p->ai_family == AF_INET6) { struct sockaddr_in6* sin6 = (struct sockaddr_in6*)p->ai_addr; *addr = sin6->sin6_addr; freeaddrinfo(result); return 0; } } freeaddrinfo(result); return -1; } ​ /** * @brief 检查Unix域Socket路径长度 * * @param path 路径 * @return int 0有效,-1过长 */ static int check_unix_path_length(const char* path) { if (!path) { return -1; } size_t len = strlen(path); if (len >= sizeof(((struct sockaddr_un*)0)->sun_path)) { return -1; } return 0; } ​ /* ==================== 公开API实现 ==================== */ ​ /** * @brief 创建IPv4地址 * * @param ip IPv4地址字符串 * @param port 端口 * @param addr 输出地址 * @return int 0成功,-1失败 */ int sock_addr_ipv4(const char* ip, uint16_t port, sock_addr_t* addr) { if (!ip || !addr) { errno = EINVAL; return -1; } memset(addr, 0, sizeof(*addr)); addr->family = SOCK_AF_INET; addr->u.ipv4.port = htons(port); struct in_addr in_addr; if (resolve_hostname(ip, &in_addr) < 0) { errno = EINVAL; return -1; } addr->u.ipv4.addr = in_addr.s_addr; return 0; } ​ /** * @brief 创建IPv6地址 * * @param ip IPv6地址字符串 * @param port 端口 * @param addr 输出地址 * @return int 0成功,-1失败 */ int sock_addr_ipv6(const char* ip, uint16_t port, sock_addr_t* addr) { if (!ip || !addr) { errno = EINVAL; return -1; } memset(addr, 0, sizeof(*addr)); addr->family = SOCK_AF_INET6; addr->u.ipv6.port = htons(port); struct in6_addr in6_addr; if (resolve_hostname_ipv6(ip, &in6_addr) < 0) { errno = EINVAL; return -1; } memcpy(addr->u.ipv6.addr, &in6_addr, sizeof(in6_addr)); return 0; } ​ /** * @brief 创建Unix域地址 * * @param path 文件路径 * @param addr 输出地址 * @return int 0成功,-1失败 */ int sock_addr_unix(const char* path, sock_addr_t* addr) { if (!path || !addr) { errno = EINVAL; return -1; } /* 检查路径长度 */ if (check_unix_path_length(path) < 0) { errno = ENAMETOOLONG; return -1; } memset(addr, 0, sizeof(*addr)); addr->family = SOCK_AF_UNIX; strncpy(addr->u.unix.path, path, sizeof(addr->u.unix.path) - 1); addr->u.unix.path[sizeof(addr->u.unix.path) - 1] = '\0'; return 0; } ​ /** * @brief 地址转字符串 * * @param addr 地址 * @param buf 输出缓冲区 * @param len 缓冲区长度 * @return int 0成功,-1失败 */ int sock_addr_str(const sock_addr_t* addr, char* buf, size_t len) { if (!addr || !buf || len == 0) { errno = EINVAL; return -1; } switch (addr->family) { case SOCK_AF_INET: { struct in_addr in_addr; in_addr.s_addr = addr->u.ipv4.addr; char ip_str[INET_ADDRSTRLEN]; if (!inet_ntop(AF_INET, &in_addr, ip_str, sizeof(ip_str))) { return -1; } uint16_t port = ntohs(addr->u.ipv4.port); snprintf(buf, len, "%s:%u", ip_str, port); break; } case SOCK_AF_INET6: { struct in6_addr in6_addr; memcpy(&in6_addr, addr->u.ipv6.addr, sizeof(in6_addr)); char ip_str[INET6_ADDRSTRLEN]; if (!inet_ntop(AF_INET6, &in6_addr, ip_str, sizeof(ip_str))) { return -1; } uint16_t port = ntohs(addr->u.ipv6.port); snprintf(buf, len, "[%s]:%u", ip_str, port); break; } case SOCK_AF_UNIX: snprintf(buf, len, "unix:%s", addr->u.unix.path); break; default: errno = EAFNOSUPPORT; return -1; } return 0; } ​ /** * @brief 从字符串解析地址 * * @param str 地址字符串(格式:ip:port 或 /path) * @param addr 输出地址 * @return int 0成功,-1失败 */ int sock_addr_parse(const char* str, sock_addr_t* addr) { if (!str || !addr) { errno = EINVAL; return -1; } /* 尝试解析为Unix域地址 */ if (str[0] == '/') { return sock_addr_unix(str, addr); } /* 尝试解析为IPv4地址:端口 */ char* colon = strchr(str, ':'); if (colon) { /* 检查是否是IPv6地址(包含多个冒号) */ char* second_colon = strchr(colon + 1, ':'); if (second_colon && str[0] == '[') { /* IPv6地址格式:[::1]:8080 */ char ip_str[INET6_ADDRSTRLEN]; char* end_bracket = strchr(str, ']'); if (!end_bracket || end_bracket > colon) { errno = EINVAL; return -1; } /* 提取IP地址部分 */ size_t ip_len = end_bracket - str - 1; if (ip_len >= sizeof(ip_str)) { errno = EINVAL; return -1; } strncpy(ip_str, str + 1, ip_len); ip_str[ip_len] = '\0'; /* 提取端口部分 */ char* port_str = colon + 1; char* endptr; long port = strtol(port_str, &endptr, 10); if (endptr == port_str || *endptr != '\0' || port < 0 || port > 65535) { errno = EINVAL; return -1; } return sock_addr_ipv6(ip_str, (uint16_t)port, addr); } else { /* IPv4地址格式:127.0.0.1:8080 */ char ip_str[INET_ADDRSTRLEN]; size_t ip_len = colon - str; if (ip_len >= sizeof(ip_str)) { errno = EINVAL; return -1; } strncpy(ip_str, str, ip_len); ip_str[ip_len] = '\0'; /* 提取端口部分 */ char* port_str = colon + 1; char* endptr; long port = strtol(port_str, &endptr, 10); if (endptr == port_str || *endptr != '\0' || port < 0 || port > 65535) { errno = EINVAL; return -1; } return sock_addr_ipv4(ip_str, (uint16_t)port, addr); } } /* 默认尝试解析为主机名(使用默认端口0) */ return sock_addr_ipv4(str, 0, addr); } ​ /** * @brief 获取错误描述 * * @param err 错误码 * @return const char* 错误描述 */ const char* sock_strerror(int err) { switch (err) { case 0: return "Success"; case EINVAL: return "Invalid argument"; case EBADF: return "Bad file descriptor"; case EACCES: return "Permission denied"; case EADDRINUSE: return "Address already in use"; case EADDRNOTAVAIL: return "Address not available"; case EAFNOSUPPORT: return "Address family not supported"; case EAGAIN: return "Resource temporarily unavailable"; case EALREADY: return "Connection already in progress"; case ECONNREFUSED: return "Connection refused"; case ECONNRESET: return "Connection reset by peer"; case EDESTADDRREQ: return "Destination address required"; case EFAULT: return "Bad address"; case EHOSTUNREACH: return "Host is unreachable"; case EINPROGRESS: return "Operation in progress"; case EISCONN: return "Socket is already connected"; case ENETDOWN: return "Network is down"; case ENETUNREACH: return "Network is unreachable"; case ENOTSOCK: return "Not a socket"; case EPROTONOSUPPORT: return "Protocol not supported"; case ETIMEDOUT: return "Connection timed out"; case EWOULDBLOCK: return "Operation would block"; case EMSGSIZE: return "Message too long"; case ENOPROTOOPT: return "Protocol option not supported"; case EPROTOTYPE: return "Wrong protocol type for socket"; case ENOTCONN: return "Socket not connected"; case ESHUTDOWN: return "Cannot send after socket shutdown"; case ENOBUFS: return "No buffer space available"; case EOPNOTSUPP: return "Operation not supported"; case EPERM: return "Operation not permitted"; case ENOMEM: return "Out of memory"; default: return strerror(err); } } ​ /** * @brief 获取本地地址信息 * * @param sock Socket句柄 * @param addr 输出地址 * @return int 0成功,-1失败 */ int sock_get_local_addr(socket_t sock, sock_addr_t* addr) { if (sock == SOCKET_INVALID || !addr) { errno = EINVAL; return -1; } struct sockaddr_storage ss; socklen_t len = sizeof(ss); if (getsockname(sock, (struct sockaddr*)&ss, &len) < 0) { return -1; } return addr_from_sys((struct sockaddr*)&ss, len, addr); } ​ /** * @brief 获取远程地址信息 * * @param sock Socket句柄 * @param addr 输出地址 * @return int 0成功,-1失败 */ int sock_get_remote_addr(socket_t sock, sock_addr_t* addr) { if (sock == SOCKET_INVALID || !addr) { errno = EINVAL; return -1; } struct sockaddr_storage ss; socklen_t len = sizeof(ss); if (getpeername(sock, (struct sockaddr*)&ss, &len) < 0) { return -1; } return addr_from_sys((struct sockaddr*)&ss, len, addr); } ​ /** * @brief 获取地址端口号 * * @param addr 地址 * @param port 输出端口号 * @return int 0成功,-1失败 */ int sock_addr_get_port(const sock_addr_t* addr, uint16_t* port) { if (!addr || !port) { errno = EINVAL; return -1; } switch (addr->family) { case SOCK_AF_INET: *port = ntohs(addr->u.ipv4.port); return 0; case SOCK_AF_INET6: *port = ntohs(addr->u.ipv6.port); return 0; case SOCK_AF_UNIX: /* Unix域Socket没有端口 */ *port = 0; return 0; default: errno = EAFNOSUPPORT; return -1; } } ​ /** * @brief 设置地址端口号 * * @param addr 地址 * @param port 端口号 * @return int 0成功,-1失败 */ int sock_addr_set_port(sock_addr_t* addr, uint16_t port) { if (!addr) { errno = EINVAL; return -1; } switch (addr->family) { case SOCK_AF_INET: addr->u.ipv4.port = htons(port); return 0; case SOCK_AF_INET6: addr->u.ipv6.port = htons(port); return 0; case SOCK_AF_UNIX: /* Unix域Socket没有端口 */ return 0; default: errno = EAFNOSUPPORT; return -1; } } ​ /** * @brief 比较两个地址是否相等 * * @param addr1 地址1 * @param addr2 地址2 * @return int 1相等,0不相等,-1错误 */ int sock_addr_equal(const sock_addr_t* addr1, const sock_addr_t* addr2) { if (!addr1 || !addr2) { return -1; } if (addr1->family != addr2->family) { return 0; } switch (addr1->family) { case SOCK_AF_INET: return (addr1->u.ipv4.addr == addr2->u.ipv4.addr && addr1->u.ipv4.port == addr2->u.ipv4.port); case SOCK_AF_INET6: return (memcmp(addr1->u.ipv6.addr, addr2->u.ipv6.addr, 16) == 0 && addr1->u.ipv6.port == addr2->u.ipv6.port); case SOCK_AF_UNIX: return (strcmp(addr1->u.unix.path, addr2->u.unix.path) == 0); default: return -1; } } ​ /** * @brief 复制地址 * * @param dst 目标地址 * @param src 源地址 * @return int 0成功,-1失败 */ int sock_addr_copy(sock_addr_t* dst, const sock_addr_t* src) { if (!dst || !src) { errno = EINVAL; return -1; } memcpy(dst, src, sizeof(*dst)); return 0; }

第五部分:编译配置和示例

5.1 Makefile 示例

# Makefile for Socket Library ​ CC = gcc CFLAGS = -Wall -Wextra -O2 -I./include -I../lib/include LDFLAGS = -L../lib -lcommon -lpthread ​ # 源文件 SRCS = src/socket_core.c src/socket_io.c src/socket_opt.c src/socket_util.c OBJS = $(SRCS:.c=.o) ​ # 目标 TARGET = libsocket.a ​ # 示例 EXAMPLES = examples/echo_server examples/echo_client ​ all: $(TARGET) $(EXAMPLES) ​ # 静态库 $(TARGET): $(OBJS) ar rcs $@ $^ ​ # 编译规则 %.o: %.c $(CC) $(CFLAGS) -c $< -o $@ ​ # 示例程序 examples/%: examples/%.c $(TARGET) $(CC) $(CFLAGS) $< -o $@ $(TARGET) $(LDFLAGS) ​ clean: rm -f $(OBJS) $(TARGET) $(EXAMPLES) ​ .PHONY: all clean

5.2 简单示例:echo_server.c

/** * @file echo_server.c * @brief 简单的Echo服务器示例 */ ​ #include "socket.h" #include <stdio.h> #include <string.h> #include <signal.h> ​ static volatile int running = 1; ​ void signal_handler(int sig) { running = 0; printf("\nShutting down...\n"); } ​ int main(int argc, char* argv[]) { /* 设置信号处理 */ signal(SIGINT, signal_handler); /* 创建TCP Socket */ socket_t sock = sock_create(SOCK_AF_INET, SOCK_TYPE_STREAM); if (sock == SOCKET_INVALID) { fprintf(stderr, "Failed to create socket: %s\n", sock_strerror(errno)); return 1; } /* 设置地址重用 */ sock_set_reuseaddr(sock, true); /* 绑定地址 */ sock_addr_t addr; if (sock_addr_ipv4("0.0.0.0", 8080, &addr) < 0) { fprintf(stderr, "Failed to create address\n"); sock_close(sock); return 1; } if (sock_bind(sock, &addr) < 0) { fprintf(stderr, "Failed to bind: %s\n", sock_strerror(errno)); sock_close(sock); return 1; } /* 开始监听 */ if (sock_listen(sock, 10) < 0) { fprintf(stderr, "Failed to listen: %s\n", sock_strerror(errno)); sock_close(sock); return 1; } printf("Echo server listening on port 8080...\n"); /* 主循环 */ while (running) { /* 接受连接 */ socket_t client = sock_accept(sock, NULL, 1000); if (client == SOCKET_INVALID) { if (errno != ETIMEDOUT && errno != EINTR) { fprintf(stderr, "Accept failed: %s\n", sock_strerror(errno)); } continue; } printf("Client connected\n"); /* 处理客户端 */ char buffer[1024]; while (running) { ssize_t n = sock_recv(client, buffer, sizeof(buffer), 1000); if (n <= 0) { if (n < 0 && errno != ETIMEDOUT) { fprintf(stderr, "Receive failed: %s\n", sock_strerror(errno)); } break; } /* 回显数据 */ buffer[n] = '\0'; printf("Received: %s", buffer); if (sock_send(client, buffer, n, 1000) < 0) { fprintf(stderr, "Send failed: %s\n", sock_strerror(errno)); break; } } printf("Client disconnected\n"); sock_close(client); } /* 清理 */ sock_close(sock); printf("Server stopped\n"); return 0; }

设计总结

这个Socket模块的设计特点:

1.高内聚

  • socket_core.c: 只处理Socket生命周期和连接管理

  • socket_io.c: 只处理数据发送和接收

  • socket_opt.c: 只处理选项设置

  • socket_util.c: 只处理地址转换和工具函数

2.低耦合

  • 每个文件独立编译,不相互依赖

  • 通过公共头文件socket.h定义接口

  • 不依赖全局状态,函数都是纯操作

3.简洁性

  • Socket句柄就是文件描述符,零封装

  • 错误处理直接使用errno

  • 接口数量最小化,只有最必要的功能

4.实用性

  • 提供超时控制

  • 处理EINTR中断

  • 支持IPv4/IPv6/Unix域

  • 线程安全(依赖基础库)

这个设计既保持了系统调用的直接性,又提供了必要的封装和错误处理,是一个平衡了简洁性和实用性的Socket库。

第三阶段 封装这个Socket模块确实有其现实意义和价值

一、封装的意义分析

1.错误处理标准化

// 原生代码 vs 封装后代码 ​ // 原生(需要手动处理各种错误情况) int sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0) { perror("socket"); return -1; } ​ // 设置非阻塞 int flags = fcntl(sock, F_GETFL, 0); if (flags < 0) { perror("fcntl"); close(sock); return -1; } if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) { perror("fcntl"); close(sock); return -1; } ​ // 设置地址重用 int reuse = 1; if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) { perror("setsockopt"); close(sock); return -1; } ​ // 封装后(统一错误处理) socket_t sock = sock_create(SOCK_AF_INET, SOCK_TYPE_STREAM); if (sock == SOCKET_INVALID) { LOG_ERROR("Failed to create socket: %s", sock_strerror(errno)); return -1; } sock_set_nonblock(sock, true); sock_set_reuseaddr(sock, true);

意义:减少50%以上的样板代码,错误处理集中化。

2.跨平台兼容性准备

虽然当前是Linux,但设计时已考虑扩展:

// 封装层可以隐藏平台差异 #ifdef __linux__ #define SOCK_CLOSE close #elif _WIN32 #define SOCK_CLOSE closesocket #endif ​ // 使用统一的接口 sock_close(sock); // 内部处理平台差异

3.资源管理自动化

// 封装可以提供RAII风格的管理 typedef struct { socket_t sock; // 自动记录创建时间、统计信息等 } managed_socket_t; ​ // 可以在close时自动清理相关资源 int sock_close(socket_t sock) { // 1. 取消所有pending操作 // 2. 关闭socket // 3. 更新统计信息 // 4. 记录日志 // 5. 通知监控系统 }

4.性能监控和统计

// 封装可以透明地添加统计 ssize_t sock_send(socket_t sock, const void* buf, size_t len, int timeout_ms) { struct timespec start, end; clock_gettime(CLOCK_MONOTONIC, &start); ssize_t result = /* 实际发送 */; clock_gettime(CLOCK_MONOTONIC, &end); double elapsed = (end.tv_sec - start.tv_sec) * 1000.0 + (end.tv_nsec - start.tv_nsec) / 1000000.0; // 记录到统计系统 update_stats(SEND_OPERATION, len, elapsed, result); return result; }

5.安全增强

// 可以添加安全检查 int sock_bind(socket_t sock, const sock_addr_t* addr) { // 1. 检查地址是否合法 if (!validate_address(addr)) { errno = EINVAL; return -1; } // 2. 检查端口权限(root需要>1024) if (geteuid() == 0 && addr->u.ipv4.port < 1024) { LOG_WARN("Root binding to privileged port"); } // 3. 记录安全日志 log_security_event("BIND", sock, addr); return bind(sock, ...); }

二、实际应用场景的价值

场景1:大规模服务器开发

// 在大型服务器中,需要: // 1. 连接池管理 // 2. 超时统一控制 // 3. 熔断机制 // 4. 监控告警 ​ // 封装可以透明实现这些 socket_t sock = connection_pool_get(); if (sock_send(sock, request, len, 100) < 0) { // 自动记录失败,触发熔断 connection_mark_bad(sock); return -1; }

场景2:协议实现

// 实现HTTP、Redis等协议时 typedef struct { socket_t sock; buffer_t input_buf; buffer_t output_buf; protocol_parser_t parser; } protocol_connection_t; ​ // 封装可以集成缓冲和解析 int http_send_request(protocol_connection_t* conn, http_request_t* req) { // 序列化请求 serialize_http_request(req, &conn->output_buf); // 发送(自动处理分片、重试) return sock_send_buffered(conn->sock, &conn->output_buf); }

场景3:测试和模拟

// 封装可以方便地mock和测试 #ifdef TESTING // 测试环境下使用mock socket socket_t mock_sock_create(...) { return create_mock_socket(); } ssize_t mock_sock_send(...) { return record_and_mock_send(); } #endif

三、具体封装价值的体现

价值1:开发效率提升

// 实现一个完整的TCP服务器 ​ // 原生:约200行代码,大量重复错误处理 // 封装:约50行代码,清晰易读 socket_t server = sock_create(SOCK_AF_INET, SOCK_TYPE_STREAM); sock_set_reuseaddr(server, true); ​ sock_addr_t addr; sock_addr_ipv4("0.0.0.0", 8080, &addr); sock_bind(server, &addr); sock_listen(server, 128); ​ while (running) { socket_t client = sock_accept(server, NULL, 100); if (client != SOCKET_INVALID) { handle_client(client); } }

价值2:维护性提升

// 当需要修改超时策略时: ​ // 没有封装:修改所有send/recv调用点(可能几十处) // 有封装:只修改sock_send/sock_recv实现 ​ // 新增TCP keepalive支持: int sock_set_keepalive(socket_t sock, int idle, int interval, int count) { // 一处实现,所有使用处自动受益 }

价值3:团队协作规范

// 确保团队使用统一的: // 1. 错误处理方式 // 2. 日志格式 // 3. 超时值 // 4. 缓冲区大小 ​ // 避免不同开发者写出风格迥异的代码

四、是否应该封装的判断标准

应该封装的情况:

  1. 项目规模大:>10个文件,多个开发者

  2. 需要重用:在多处使用相似代码

  3. 需要维护:长期维护的项目

  4. 需要监控:需要统计和诊断

  5. 团队协作:需要统一编码规范

不应该封装的情况:

  1. 一次性脚本:用完就丢

  2. 极端性能敏感:每个CPU周期都重要

  3. 特殊系统调用:需要直接控制

  4. 学习目的:理解底层机制

五、对当前项目的建议

基于common基础库,这个Socket封装的价值:

  1. 与现有基础设施集成

    // 直接使用common库的日志、内存、线程 LOG_DEBUG("SOCKET", "Created socket fd=%d", fd); void* buf = common_malloc(size); common_mutex_lock(&socket_mutex);
  2. 统一项目管理

    • 一致的代码风格

    • 统一的错误处理

    • 集成的监控统计

  3. 为未来扩展预留

    • 添加SSL/TLS支持

    • 支持IPv6过渡

    • 集成连接池

    • 添加重试机制

结论

封装的意义在于

  • 不是为了封装而封装

  • 不是隐藏系统调用(仍可直接访问fd)

  • 为了提供一致的开发体验

  • 为了减少重复代码

  • 为了集中维护通用逻辑

  • 为了便于监控和调试

对于项目,这个适度的封装在以下方面提供了价值:

  1. 错误处理标准化(利用common库)

  2. 资源管理简化(自动清理)

  3. 可维护性提升(集中修改)

  4. 可测试性增强(易于mock)

  5. 团队协作友好(统一接口)

这是一个务实的封装:不过度设计,不隐藏重要细节,但提供了实际开发中需要的便利性和一致性。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/20 14:07:07

5.Aop

5.1 Aop简介AOP的全程是Aspect Oriented Programming&#xff0c;即面向切面编程。是实现功能统一维护的一种技术&#xff0c;它将业务逻辑的各个部分进行隔离&#xff0c;使开发人员在编写业务逻辑时可以专心于核心业务&#xff0c;从而提高了开发效率作用&#xff1a;在不修改…

作者头像 李华
网站建设 2026/4/19 5:28:16

使用Ollama本地运行Qwen3-14B大模型|附安装包获取方式

使用Ollama本地运行Qwen3-14B大模型&#xff5c;附安装包获取方式 在生成式AI浪潮席卷各行各业的今天&#xff0c;越来越多企业开始尝试将大语言模型&#xff08;LLM&#xff09;融入业务流程。但当你真正着手落地时&#xff0c;往往会发现&#xff1a;公有云API虽然便捷&#…

作者头像 李华
网站建设 2026/4/18 8:24:42

git下载安装教程升级版:加入vLLM推理加速模块

vLLM推理加速引擎实战部署&#xff1a;从Git配置到高性能模型服务构建 在当前大语言模型&#xff08;LLM&#xff09;广泛应用的背景下&#xff0c;如何将一个强大的开源模型真正“跑起来”&#xff0c;并且稳定、高效地服务于生产环境&#xff0c;已经成为企业AI团队面临的核心…

作者头像 李华
网站建设 2026/4/19 20:33:20

Java工程智能化破局:飞算科技JavaAI构建开发新范式

在软件开发领域&#xff0c;Java作为应用范围广泛的编程语言&#xff0c;其工程开发环节正面临效率瓶颈凸显、代码质量波动、人力成本攀升等多重挑战。推动Java工程智能化升级&#xff0c;已成为行业实现高质量发展亟待解决的核心议题。飞算数智科技&#xff08;深圳&#xff0…

作者头像 李华
网站建设 2026/4/16 11:49:00

使用DiskInfo下载官网模型文件:Stable Diffusion 3.5 FP8资源获取路径

使用DiskInfo下载官网模型文件&#xff1a;Stable Diffusion 3.5 FP8资源获取路径 在AI生成图像技术飞速演进的今天&#xff0c;越来越多的内容创作者、开发者和企业开始尝试部署本地化的文生图系统。然而&#xff0c;一个现实问题始终横亘在理想与落地之间&#xff1a;如何在消…

作者头像 李华
网站建设 2026/4/16 11:57:32

移动端UI组件的高效应用与性能优化策略

移动端UI组件的高效应用与性能优化策略 【免费下载链接】coloruicss 鲜亮的高饱和色彩&#xff0c;专注视觉的小程序组件库 项目地址: https://gitcode.com/gh_mirrors/co/coloruicss 在移动应用开发中&#xff0c;UI组件的合理运用直接影响用户体验和应用性能。当前开发…

作者头像 李华