2026/3/27 16:43:34
网站建设
项目流程
做一个网站设计要多久,文创产品营销方案,做哪类视频网站需要视频证书,重庆招投标信息网一、TCP 的通信模型#xff1a;网络应用的架构底色TCP 的通信场景并非单一模式#xff0c;不同模型对应不同的业务逻辑与技术挑战#xff1a;1. CS 模型#xff08;Client-Server#xff0c;客户端 - 服务器#xff09;核心逻辑#xff1a;中心化架构#xff0c;服务器…一、TCP 的通信模型网络应用的架构底色TCP 的通信场景并非单一模式不同模型对应不同的业务逻辑与技术挑战1. CS 模型Client-Server客户端 - 服务器核心逻辑中心化架构服务器被动监听连接客户端主动发起请求。典型的 “请求 - 响应” 模式比如你打开微信 App客户端连接微信服务器发送消息、接收消息都基于这个模型。关键特点客户端是专用程序如微信、QQ应用层协议可自定义比如微信的私有通信协议资源可存于本地如微信的聊天记录缓存功能相对复杂服务器需处理高并发连接这也是epoll、select等 I/O 多路复用技术的诞生背景。2. BS 模型Browser-Server浏览器 - 服务器核心逻辑CS 模型的 “特例”但因 Web 的普及成为独立分支。客户端是通用浏览器如 Chrome应用层固定使用 HTTP/HTTPS 协议。关键特点客户端是通用工具无需安装专用软件资源由服务器统一提供如网页代码、图片功能相对轻量化依赖 HTTP 的标准化特性同时也因 HTTP 的 “无状态” 特性催生出 Cookie、Session 等状态保持机制。3. P2P 模型Peer-to-Peer对等网络核心逻辑去中心化架构每个节点Peer既是 “客户端”下载数据也是 “服务器”上传数据。典型场景是迅雷下载 —— 你下载文件时既从其他用户的设备Peer获取数据也会把自己已下载的部分分享给其他用户。关键特点无中心化服务器节点之间直接通信充分利用网络带宽但也面临 “节点发现”“动态拓扑管理” 等技术难点。二、TCP 的核心特征可靠传输的底层逻辑TCP 被称为 “可靠协议”其所有设计都围绕 “如何在不可靠的网络中传递可靠的字节流” 展开核心特征可总结为 7 点也是理解 TCP 黏包、可靠性的关键1. 面向连接三次握手建连四次挥手断连核心逻辑TCP 通信前必须先建立连接连接建立后链路会一直保持直到其中一方主动表示断开。三次握手客户端发SYN→服务器回SYNACK→客户端回ACK确认双方收发能力正常四次挥手主动方发FIN→被动方回ACK→被动方发FIN→主动方回ACK优雅释放连接关键特性若对方端口断开连接另一方可以通过四次挥手机制感知到连接状态变化。2. 可靠传输应答 重传代价是实时性与资源消耗TCP 的 “可靠” 是通过两套核心机制实现的但也带来了明显代价应答机制ACK接收方收到数据后会返回 “确认报文”ACK通过ack num确认号告诉发送方 “已收到序号 XX 之前的所有数据”超时重传发送方如果在指定时间内没收到 ACK会重新发送该数据段代价相比 UDPTCP 实时性弱、网络资源消耗大 ——“可靠” 是用 “确认、重传” 的额外开销换来的。3. 流式套接字数据无边界黏包的根源核心特点TCP 传输的是 “连续、有序的字节流”数据本身没有天然边界黏包现象发送方可以分多次发送数据比如分 3 次发 “你”“好”“世界”接收方可能一次性接收所有数据收到 “你好世界”导致数据无法正常解析核心结论发送次数和接收次数不需要对应这是 TCP 流式传输的核心特征。4. 全双工通信双缓冲区双向独立收发核心逻辑通信双方都有独立的 “发送缓冲区” 和 “接收缓冲区”关键特性双方可同时向对方发送数据无需等待一方发送完成再接收数据传输双向且独立。5. 写阻塞机制默认 64K 缓冲区防止数据溢出TCP 默认发送缓冲区大小为 64K当缓冲区被写满时send()等发送操作会被阻塞直到缓冲区有空闲空间避免数据无序溢出。6. 滑动窗口流量控制 高效传输这是 TCP “高性能” 与 “可靠性” 的平衡点接收方通过 ACK 告诉发送方 “当前接收缓冲区还能存多少数据”接收窗口rwnd发送方根据rwnd调整发送速度避免接收方缓冲区溢出流量控制同时发送方可以连续发送多个数据段无需每发一个就等待 ACK批量传输大幅提升效率。7. 有序传输按序列号重组数据TCP 会为每个字节的数据分配唯一的序列号接收方会按序列号重新排列乱序到达的数据段保证最终拿到的是有序的完整数据。三、TCP 的 “经典坑”黏包问题及解决方案黏包是 TCP 流式传输的必然产物 —— 数据无边界导致发送方多次发送的小数据被接收方合并接收核心解决思路是给字节流 “人工加边界”主流方案有 3 种1. 自定义结束标志在数据末尾加固定的 “结束符”比如\n、|接收方读取数据时以该标志为边界拆分。优点实现简单缺点若数据本身包含结束符会导致误拆分比如文本中包含\n。2. 固定数据长度约定每次传输的数据长度固定比如每次发 100 字节接收方按固定长度读取数据不足部分补空字符。优点逻辑清晰缺点数据长度不固定时会浪费带宽比如实际数据只有 10 字节也要补 90 字节的空字符。3. 自定义应用层协议工业级方案设计包含 “头部 数据体” 的结构化协议头部中明确标注 “数据长度”接收方先读头部获取长度再按长度读数据体。典型格式如起始标志AA 数据长度Num 数据内容Data 校验位 结束标志BB优点适配所有场景可扩展比如加数据类型、版本号缺点需要自定义解析逻辑。四、TCP 通信核心函数全解析在 Linux 系统中TCP 通信的核心是一系列系统调用函数下面严格按照核心定义拆解每个函数的功能、参数和使用逻辑1. socket () - 创建套接字描述符函数原型int socket(int domain, int type, int protocol);核心功能程序向内核提出创建一个基于内存的套接字描述符本质是申请一个用于网络通信的 “内核资源标识”。参数详解参数取值 / 说明核心作用domainPF_INET/AF_INET互联网程序IPv4PF_UNIX/AF_UNIX单机程序本地通信指定地址族决定通信的范围跨网络 / 仅本机typeSOCK_STREAM流式套接字对应 TCPSOCK_DGRAM用户数据报套接字对应 UDPSOCK_RAW原始套接字对应 IP 层指定套接字类型决定通信的传输方式protocol0表示自动适配应用层协议指定具体通信协议填 0 由系统自动匹配返回值成功返回申请的套接字 ID非负整数类似文件描述符失败返回-1。2. bind () - 绑定套接字与接口地址函数原型int bind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen);核心功能若在服务器端调用将参数 1 的套接字描述符与参数 2 指定的接口地址IP 端口关联用于从该接口接收客户端数据客户端一般无需调用系统自动分配随机端口。返回值成功返回0失败返回-1常见错误端口被占用。3. listen () - 监听套接字等待连接函数原型int listen(int sockfd, int backlog);核心功能在参数 1 的套接字 ID 上监听等待客户端的连接请求仅服务器端调用。参数详解参数说明sockfd已绑定的套接字 IDbacklog允许处于三次握手状态的连接排队数如 5、10返回值成功返回0失败返回-1。4. accept () - 接入客户端有效连接函数原型int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);核心功能从已监听到的连接队列中取出有效的客户端连接并接入到当前程序仅服务器端调用。参数详解参数说明sockfd处于监听状态的套接字 IDaddr可选参数- 为NULL不获取客户端信息不论客户端是谁都接入- 非NULL传入结构体变量地址函数执行后将客户端的 IP / 端口信息存入该变量addrlen可选参数- 若addr为NULL该值也为NULL- 若addr非NULL需先赋值len sizeof(struct sockaddr)再传入len返回值成功返回用于通信的新套接字 ID后续与该客户端的所有通信都基于此 ID失败返回-1。5. recv () - 接收套接字数据函数原型ssize_t recv(int sockfd, void *buf, size_t len, int flags);核心功能从指定的sockfd套接字中以flags方式获取长度为len字节的数据存入buf内存中。参数详解参数说明sockfd通信套接字 ID- 服务器端accept()返回的新 ID- 客户端socket()返回的原 IDbuf本地内存缓冲区数组 / 动态内存用于存储接收的数据len期望接收的数据长度缓冲区最大容量flags0阻塞接受无数据则等待返回值成功返回实际接收的字节长度通常≤len失败返回-1返回0表示对方关闭了连接。6. send () - 发送数据到套接字函数原型int send(int sockfd, const void *msg, size_t len, int flags);核心功能从msg指向的内存中获取长度为len的数据以flags方式写入到sockfd对应的套接字中。返回值成功返回实际发送的字符长度失败返回-1。7. connect () - 客户端发起连接函数原型int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);核心功能固定由客户端使用向目标主机服务器发起 TCP 连接请求完成三次握手。返回值成功返回0失败返回-1常见错误连接被拒绝、网络超时。8. close () - 关闭套接字函数原型int close(int fd);核心功能关闭套接字描述符释放内核资源触发 TCP 四次挥手断开连接。返回值成功返回0失败返回-1。五、TCP 与 UDP 的核心区别工业级对比TCP 和 UDP 是传输层的两大核心协议设计理念完全不同适用场景也天差地别核心区别可总结为下表对比维度TCPUDP连接特性面向连接三次握手建连四次挥手断连无连接无需建连直接发数据传输可靠性可靠传输ACK 应答 超时重传 有序重组不可靠传输无确认、无重传可能丢包 / 乱序数据边界无边界流式字节流易黏包有边界数据报模式一次发送 一次接收收发次数发送次数≠接收次数发送次数 接收次数实时性弱确认、重传增加延迟强无额外开销延迟低资源消耗大维护连接、缓冲区、序列号等小仅封装数据报无额外状态通信模式全双工双缓冲区双向同时收发全双工但无连接状态阻塞特性有写阻塞64K 发送缓冲区无写阻塞发送缓冲区满则直接丢包连接感知可感知对方断开四次挥手无法感知对方状态发数据才知道是否可达适用场景要求可靠的场景文件传输、网页浏览、数据库通信、即时通讯要求实时的场景音视频直播、游戏、物联网数据上报六、TCP 通信的代码实践完整示例TCP函数调用顺序1. 服务器端多连接版#include stdio.h #include stdlib.h #include string.h #include unistd.h #include sys/socket.h #include netinet/in.h #include arpa/inet.h #include pthread.h #define PORT 8080 #define BUF_SIZE 1024 // 处理单个客户端连接的线程函数 void *handle_client(void *arg) { int conn_fd *(int *)arg; free(arg); // 释放传参的内存 char buf[BUF_SIZE]; ssize_t recv_len; while ((recv_len recv(conn_fd, buf, BUF_SIZE-1, 0)) 0) { buf[recv_len] \0; printf(收到客户端消息%s\n, buf); // 回复客户端 char reply[BUF_SIZE]; snprintf(reply, BUF_SIZE, 服务器已收到%s, buf); send(conn_fd, reply, strlen(reply), 0); } close(conn_fd); printf(客户端连接已断开感知到连接关闭\n); pthread_exit(NULL); } int main() { // 1. 创建套接字互联网程序TCP流式套接字 int listen_fd socket(PF_INET, SOCK_STREAM, 0); if (listen_fd -1) { perror(socket failed); exit(EXIT_FAILURE); } // 2. 设置端口复用避免端口占用问题 int opt 1; setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, opt, sizeof(opt)); // 绑定IP和端口关联套接字与接口地址 struct sockaddr_in server_addr { .sin_family AF_INET, .sin_addr.s_addr INADDR_ANY, // 监听所有网卡 .sin_port htons(PORT) }; if (bind(listen_fd, (struct sockaddr*)server_addr, sizeof(server_addr)) -1) { perror(bind failed); close(listen_fd); exit(EXIT_FAILURE); } // 3. 监听套接字允许5个三次握手排队连接 if (listen(listen_fd, 5) -1) { perror(listen failed); close(listen_fd); exit(EXIT_FAILURE); } printf(服务器启动监听端口 %d...\n, PORT); // 4. 循环接受客户端连接 while (1) { struct sockaddr_in client_addr; socklen_t client_len sizeof(struct sockaddr); int *conn_fd malloc(sizeof(int)); // 接入客户端连接获取通信套接字ID *conn_fd accept(listen_fd, (struct sockaddr*)client_addr, client_len); if (*conn_fd -1) { perror(accept failed); free(conn_fd); continue; } printf(客户端 %s:%d 已连接三次握手完成\n, inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); // 5. 创建线程处理该客户端通信 pthread_t tid; if (pthread_create(tid, NULL, handle_client, conn_fd) ! 0) { perror(pthread_create failed); close(*conn_fd); free(conn_fd); } pthread_detach(tid); // 分离线程自动回收资源 } close(listen_fd); return 0; }2. 客户端#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 SERVER_IP 127.0.0.1 #define SERVER_PORT 8080 #define BUF_SIZE 1024 int main() { // 1. 创建套接字互联网程序TCP流式套接字 int sock_fd socket(PF_INET, SOCK_STREAM, 0); if (sock_fd -1) { perror(socket failed); exit(EXIT_FAILURE); } // 2. 向服务器发起连接请求三次握手 struct sockaddr_in server_addr { .sin_family AF_INET, .sin_addr.s_addr inet_addr(SERVER_IP), .sin_port htons(SERVER_PORT) }; if (connect(sock_fd, (struct sockaddr*)server_addr, sizeof(server_addr)) -1) { perror(connect failed); close(sock_fd); exit(EXIT_FAILURE); } printf(已连接到服务器 %s:%d三次握手完成\n, SERVER_IP, SERVER_PORT); // 3. 收发数据流式传输发送/接收次数无需对应 char buf[BUF_SIZE]; printf(请输入消息输入exit退出); while (fgets(buf, BUF_SIZE, stdin) ! NULL) { buf[strcspn(buf, \n)] \0; // 去掉换行符 if (strcmp(buf, exit) 0) break; // 发送数据到服务器写阻塞缓冲区满则等待 send(sock_fd, buf, strlen(buf), 0); // 接收服务器回复 ssize_t recv_len recv(sock_fd, buf, BUF_SIZE-1, 0); if (recv_len 0) { buf[recv_len] \0; printf(服务器回复%s\n, buf); } else if (recv_len 0) { printf(服务器已断开连接感知到四次挥手\n); break; } printf(请输入消息输入exit退出); } // 4. 关闭套接字触发四次挥手 close(sock_fd); printf(已断开与服务器的连接四次挥手完成\n); return 0; }七、总结TCP 核心特征面向连接三次握手 / 四次挥手、可靠传输ACK 超时重传、流式无边界黏包根源、全双工双缓冲区、发送 / 接收次数不对应、64K 写阻塞、可感知连接断开黏包解决方案自定义结束标志简单、固定长度低效、自定义应用层协议工业级TCP vs UDPTCP 重可靠适合文件 / 网页UDP 重实时适合音视频 / 游戏核心差异在连接性、可靠性和数据边界。