1. Linux网络套接字基础概念网络套接字(Socket)是Linux系统网络编程的核心概念它本质上是一个通信端点就像我们日常生活中使用的电话插座一样。在Linux系统中套接字用一个整数来标识这个整数称为套接字描述符类似于文件描述符。通过这个描述符我们可以进行各种网络操作如建立连接、发送和接收数据等。Socket最早出现在1983年的BSD 4.2系统中后来成为网络编程的事实标准。Linux继承了BSD的Socket实现并在此基础上进行了扩展。Socket之所以如此重要是因为它提供了一种统一的接口使得应用程序可以透明地使用各种网络协议进行通信。在Linux中Socket不仅仅用于网络通信还可以用于本地进程间通信。但最常见的用途还是网络编程特别是TCP/IP协议栈的实现。通过Socket API我们可以轻松地开发出各种网络应用程序如Web服务器、邮件客户端、即时通讯工具等。2. Socket类型与协议族2.1 主要协议族创建Socket时我们需要指定协议族(domain)这决定了Socket可以使用的通信特性。Linux支持多种协议族最常见的有AF_INETIPv4协议族这是目前使用最广泛的协议族AF_INET6IPv6协议族下一代互联网协议AF_UNIX/AF_LOCAL本地通信协议族用于同一台主机上的进程间通信AF_PACKET底层数据包接口可用于原始套接字编程在实际开发中我们最常用的是AF_INET和AF_INET6。选择哪个协议族取决于你的网络环境和应用需求。如果你的网络环境支持IPv6建议优先使用AF_INET6因为它能提供更大的地址空间和更好的安全性。2.2 Socket服务类型除了协议族我们还需要指定Socket的服务类型(type)SOCK_STREAM面向连接的流式套接字提供可靠的、有序的双向字节流服务对应TCP协议SOCK_DGRAM无连接的数据报套接字提供不可靠的消息传递服务对应UDP协议SOCK_RAW原始套接字允许直接访问底层协议对于大多数应用来说SOCK_STREAM和SOCK_DGRAM是最常用的选择。如果你需要可靠的数据传输如文件传输、网页浏览等应该选择SOCK_STREAM如果你需要实时性更高但对可靠性要求不高的应用如视频会议、在线游戏等可以选择SOCK_DGRAM。3. 核心Socket API详解3.1 socket()函数socket()函数用于创建一个Socket描述符其原型如下int socket(int domain, int type, int protocol);参数说明domain协议族如AF_INET、AF_INET6等type服务类型如SOCK_STREAM、SOCK_DGRAM等protocol通常设为0表示使用默认协议返回值成功时返回非负的Socket描述符失败时返回-1并设置errno在实际编程中创建TCP Socket的典型代码如下int sockfd socket(AF_INET, SOCK_STREAM, 0); if (sockfd 0) { perror(socket creation failed); exit(EXIT_FAILURE); }3.2 bind()函数bind()函数用于将Socket与特定的IP地址和端口号绑定其原型如下int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);参数说明sockfdSocket描述符addr指向sockaddr结构体的指针包含IP和端口信息addrlenaddr结构体的大小常用的sockaddr_in结构体定义如下struct sockaddr_in { sa_family_t sin_family; // 地址族如AF_INET in_port_t sin_port; // 端口号网络字节序 struct in_addr sin_addr; // IP地址 unsigned char sin_zero[8];// 填充字段 };绑定端口的示例代码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); // 绑定8080端口 if (bind(sockfd, (struct sockaddr *)servaddr, sizeof(servaddr)) 0) { perror(bind failed); exit(EXIT_FAILURE); }注意在Linux系统中1024以下的端口号需要root权限才能绑定。开发测试时建议使用1024以上的端口号。3.3 listen()函数listen()函数用于将Socket设置为被动监听模式等待客户端连接请求其原型如下int listen(int sockfd, int backlog);参数说明sockfd已绑定的Socket描述符backlog连接请求队列的最大长度listen()函数不会阻塞它只是设置Socket的状态。实际的连接请求会被放入队列中等待处理。backlog参数指定了这个队列的最大长度如果队列已满新的连接请求可能会被拒绝。典型用法if (listen(sockfd, 5) 0) { perror(listen failed); exit(EXIT_FAILURE); }3.4 accept()函数accept()函数用于接受客户端的连接请求其原型如下int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);参数说明sockfd处于监听状态的Socket描述符addr用于存储客户端地址信息的结构体指针addrlenaddr结构体大小的指针返回值成功时返回一个新的Socket描述符用于与客户端通信失败时返回-1并设置errnoaccept()函数会阻塞直到有客户端连接请求到达。一旦接受连接它会返回一个新的Socket描述符服务器应该使用这个新的描述符与客户端通信而原始的监听Socket继续用于接受其他连接。典型用法struct sockaddr_in cliaddr; socklen_t clilen sizeof(cliaddr); int connfd accept(sockfd, (struct sockaddr *)cliaddr, clilen); if (connfd 0) { perror(accept failed); exit(EXIT_FAILURE); }3.5 connect()函数connect()函数用于客户端发起连接请求其原型如下int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);参数说明sockfdSocket描述符addr服务器地址信息addrlenaddr结构体的大小对于TCP Socketconnect()会触发三次握手过程对于UDP Socket它只是记录目标地址供后续send()/sendto()使用。典型用法struct sockaddr_in servaddr; memset(servaddr, 0, sizeof(servaddr)); servaddr.sin_family AF_INET; servaddr.sin_port htons(8080); inet_pton(AF_INET, 127.0.0.1, servaddr.sin_addr); if (connect(sockfd, (struct sockaddr *)servaddr, sizeof(servaddr)) 0) { perror(connect failed); exit(EXIT_FAILURE); }4. 数据传输函数4.1 send()/write()函数send()和write()函数都可用于发送数据但send()提供了更多的控制选项ssize_t send(int sockfd, const void *buf, size_t len, int flags); ssize_t write(int sockfd, const void *buf, size_t len);flags参数常用的值MSG_OOB发送带外数据MSG_DONTWAIT非阻塞操作MSG_NOSIGNAL发送失败时不产生SIGPIPE信号典型用法char *msg Hello, server!; ssize_t n send(sockfd, msg, strlen(msg), 0); if (n 0) { perror(send failed); exit(EXIT_FAILURE); }4.2 recv()/read()函数recv()和read()函数用于接收数据ssize_t recv(int sockfd, void *buf, size_t len, int flags); ssize_t read(int sockfd, void *buf, size_t len);flags参数常用的值MSG_PEEK查看数据但不从缓冲区移除MSG_WAITALL等待所有数据到达MSG_DONTWAIT非阻塞操作典型用法char buffer[1024]; ssize_t n recv(sockfd, buffer, sizeof(buffer), 0); if (n 0) { perror(recv failed); exit(EXIT_FAILURE); } buffer[n] \0; // 确保字符串正确终止5. 完整TCP客户端/服务器示例5.1 TCP服务器实现#include stdio.h #include stdlib.h #include string.h #include unistd.h #include sys/socket.h #include netinet/in.h #include arpa/inet.h #define PORT 8080 #define BUFFER_SIZE 1024 int main() { int server_fd, new_socket; struct sockaddr_in address; int opt 1; int addrlen sizeof(address); char buffer[BUFFER_SIZE] {0}; // 创建Socket if ((server_fd socket(AF_INET, SOCK_STREAM, 0)) 0) { perror(socket failed); exit(EXIT_FAILURE); } // 设置Socket选项 if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, opt, sizeof(opt))) { perror(setsockopt failed); exit(EXIT_FAILURE); } address.sin_family AF_INET; address.sin_addr.s_addr INADDR_ANY; address.sin_port htons(PORT); // 绑定Socket if (bind(server_fd, (struct sockaddr *)address, sizeof(address)) 0) { perror(bind failed); exit(EXIT_FAILURE); } // 开始监听 if (listen(server_fd, 3) 0) { perror(listen failed); exit(EXIT_FAILURE); } printf(Server listening on port %d...\n, PORT); // 接受连接 if ((new_socket accept(server_fd, (struct sockaddr *)address, (socklen_t*)addrlen)) 0) { perror(accept failed); exit(EXIT_FAILURE); } printf(Connection accepted from %s:%d\n, inet_ntoa(address.sin_addr), ntohs(address.sin_port)); // 读取客户端数据 ssize_t valread read(new_socket, buffer, BUFFER_SIZE); printf(Received: %s\n, buffer); // 发送响应 char *response Hello from server; send(new_socket, response, strlen(response), 0); printf(Response sent\n); close(new_socket); close(server_fd); return 0; }5.2 TCP客户端实现#include stdio.h #include stdlib.h #include string.h #include unistd.h #include sys/socket.h #include netinet/in.h #include arpa/inet.h #define PORT 8080 #define BUFFER_SIZE 1024 int main() { int sock 0; struct sockaddr_in serv_addr; char buffer[BUFFER_SIZE] {0}; // 创建Socket if ((sock socket(AF_INET, SOCK_STREAM, 0)) 0) { perror(socket creation failed); exit(EXIT_FAILURE); } serv_addr.sin_family AF_INET; serv_addr.sin_port htons(PORT); // 转换IP地址 if (inet_pton(AF_INET, 127.0.0.1, serv_addr.sin_addr) 0) { perror(invalid address); exit(EXIT_FAILURE); } // 连接服务器 if (connect(sock, (struct sockaddr *)serv_addr, sizeof(serv_addr)) 0) { perror(connection failed); exit(EXIT_FAILURE); } // 发送数据 char *message Hello from client; send(sock, message, strlen(message), 0); printf(Message sent\n); // 接收响应 ssize_t valread read(sock, buffer, BUFFER_SIZE); printf(Server response: %s\n, buffer); close(sock); return 0; }6. 高级Socket编程技巧6.1 非阻塞I/O通过设置Socket为非阻塞模式可以使I/O操作立即返回而不必等待操作完成int flags fcntl(sockfd, F_GETFL, 0); fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);在非阻塞模式下如果操作不能立即完成函数会返回-1并设置errno为EAGAIN或EWOULDBLOCK。6.2 多路复用I/O使用select()、poll()或epoll()可以同时监控多个Socket的活动fd_set readfds; FD_ZERO(readfds); FD_SET(sockfd, readfds); struct timeval timeout; timeout.tv_sec 5; timeout.tv_usec 0; int activity select(sockfd 1, readfds, NULL, NULL, timeout); if (activity 0 FD_ISSET(sockfd, readfds)) { // Socket可读 }epoll是Linux特有的高效多路复用机制特别适合处理大量并发连接。6.3 Socket选项设置通过setsockopt()可以设置各种Socket选项int optval 1; setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, optval, sizeof(optval));常用选项包括SO_REUSEADDR允许重用本地地址SO_KEEPALIVE启用TCP保活机制SO_RCVBUF/SO_SNDBUF设置接收/发送缓冲区大小TCP_NODELAY禁用Nagle算法7. 常见问题与解决方案7.1 地址已在使用(Address already in use)这个问题通常发生在服务器重启后立即尝试绑定相同端口时。解决方案是设置SO_REUSEADDR选项int optval 1; setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, optval, sizeof(optval));7.2 连接被拒绝(Connection refused)这通常意味着目标服务器没有在指定端口上运行或者防火墙阻止了连接。检查服务器程序是否正在运行服务器是否监听正确的端口防火墙设置是否正确7.3 连接超时(Connection timed out)可能原因包括网络不通服务器没有响应中间路由器或防火墙阻止了连接解决方案使用ping测试网络连通性使用telnet或nc测试端口是否开放检查服务器日志7.4 资源暂时不可用(Resource temporarily unavailable)在非阻塞模式下当操作不能立即完成时会出现这个错误。正确处理方式是稍后重试或使用多路复用机制等待Socket就绪。8. 性能优化建议8.1 缓冲区大小调整根据应用特点调整Socket缓冲区大小可以提高性能int bufsize 1024 * 1024; // 1MB setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, bufsize, sizeof(bufsize)); setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, bufsize, sizeof(bufsize));8.2 禁用Nagle算法对于实时性要求高的应用可以禁用Nagle算法减少延迟int flag 1; setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, flag, sizeof(flag));8.3 使用零拷贝技术对于高性能服务器可以考虑使用sendfile()等零拷贝技术减少数据拷贝开销。8.4 连接池管理对于客户端程序使用连接池可以避免频繁创建和销毁连接的开销。9. 安全注意事项9.1 输入验证永远不要信任来自网络的输入数据。对所有接收到的数据进行严格验证防止缓冲区溢出等安全问题。9.2 权限控制服务器程序应该以最小必要权限运行避免使用root权限运行网络服务。9.3 加密通信对于敏感数据应该使用SSL/TLS等加密技术保护通信安全。9.4 防止DoS攻击实现适当的资源管理和请求限制机制防止拒绝服务攻击。
企业数字化 ERP 产品动态
相关推荐
14个免费AI通道聚合网关:自动路由与健康检查实战 1. 为什么要把 14 个免费通道塞进一个入口 第一次看到"14 个免费通道并成 1 个入口"这个说法,我脑子里蹦出来的不是"哇好厉害",而是"这不是自找麻烦吗"。免费模型这东西,单拎出来哪个都能用,为什么… · 2026/11/3 13:38:30
Buzz工具:轻量级HTTP压测实战指南 我无法根据当前输入生成符合要求的博文。原因在于:您提供的输入内容中,项目标题仅为“buzz”,且后续所有字段(项目正文、关键词、摘要描述)均为空或未提供有效信息。同时,“相关热搜词”与“最新网络热词”… · 2026/11/3 13:38:30
ISTQB-CT-AI大纲:AI测试工程师的标准化质量保障指南 简介:本资源是ISTQB官方发布的《认证测试工程师—人工智能测试大纲》(CT-AI-1.0-CN-1.0)中文版PDF文档,面向AI测试工程师、质量保障人员及希望系统掌握AI系统验证方法的专业人士,解决当前AI模型可解释性弱、质量特征难… · 2026/11/3 13:38:30
大模型数据预处理实战:从数据源到动态Padding的流水线方案 「昇思 MindSpore 大模型」这句话里,真正决定一次微调能不能顺利跑通的环节,往往不是模型结构,而是那串看起来平平无奇的 mindspore.dataset 。我在好几个大模型项目里见过同一种症状:训练脚本没问题,模型结构没问题… · 2026/11/3 14:25:24
OpenClaw+React+Node.js构建AI应用新范式 1. “Paperclip”不是回形针:一个被误读的AI工程代号与真实技术图谱最近在多个技术社区和开发者群聊里,频繁看到“paperclip”这个词被当作某个新工具、框架或开源项目来讨论。有人问“paperclip怎么安装”,有人发帖求“paperclip React集成示… · 2026/11/3 14:25:24
reverse-skill:实战逆向能力的四大支柱与AI增强路径 1. “reverse-skill”不是新造词,而是安全从业者内部的暗语式共识“reverse-skill”这个词乍看像某个开源项目名或AI模型代号,但在我过去十年混迹于二进制分析、漏洞研究和红队实战的圈子里,它从来不是官方术语——而是一群人用缩写技能组合形… · 2026/11/3 14:25:24
FPGA SelectIO IP核配置、XDC约束与tap扫描避坑指南 简介:面向 Xilinx FPGA 接口设计者的一份 SelectIO IP 核配置参考文档,围绕 SelectIO Interface Wizard v5.1(PG070)展开,适合已具备基本 Vivado 使用经验、需要为网络协议类接口定制 I/O 电气标准与数据通路的工程师查… · 2026/11/3 14:25:24
FPGA SelectIO IP核配置与LVDS源同步接收时序避坑 简介:围绕Xilinx FPGA SelectIO接口技术展开的PDF文档,面向需要掌握可编程I/O配置的FPGA设计者与接口调试人员。内容从SelectIO IP核概述切入,梳理LVCMOS、LVTTL、LVDS、HSTL、SSTL等电气标准,以及数据速率、功耗、信号完整性和LU… · 2026/11/3 14:25:24
推荐系统用户分群策略:方法与实战解析 1. 推荐系统中的用户分群策略概述在当今互联网产品中,推荐系统已经成为提升用户体验和商业价值的关键组件。而用户分群作为推荐系统的核心技术之一,直接影响着个性化推荐的精准度和运营效率。简单来说,用户分群就是根据用户的行为特征、兴趣偏… · 2026/11/3 14:24:22
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 6:04:24
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 5:52:46
STM32F411RE与MAX14431实现SPI隔离:工业现场抗共模干扰实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59
ESP32+大模型AI硬件开发:从Demo到产品的8个工程问题 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59
基于MAX14431BAEE+与PIC18F85J10的SPI隔离方案设计与实践 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 6:04:24
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 5:52:46