计算机网络_UDP和TCP

发布时间:2026/9/23 1:12:52

计算机网络_UDP和TCP TCP传输控制协议和 UDP用户数据报协议是TCP/IP 协议簇中传输层的两大核心协议它们的设计理念完全相反 ——TCP 追求可靠传输UDP 追求高效传输这直接决定了它们在不同业务场景尤其是游戏开发中的选型差异。下面从协议本质、核心机制、详细区别、适用场景四个维度给你讲透两者的差异。一、协议本质与核心设计目标传输层的核心作用是“端到端” 的数据传输从一台主机的应用程序传到另一台主机的应用程序而 TCP 和 UDP 的目标完全不同TCP设计目标是“保证数据 100% 准确、有序地到达目的地”哪怕牺牲传输速度。可以类比成“挂号信”寄件前要确认收件人地址有效建立连接寄件后要等收件人签收确认应答丢件了要重寄重传机制全程可追踪。UDP设计目标是“以最快速度把数据发出去不保证是否到达”哪怕牺牲可靠性。可以类比成“明信片”写好地址直接投递不用确认收件人是否收到丢了也不补寄速度快、开销小。二、TCP 详解面向连接的可靠传输协议TCP 是面向连接、可靠、基于字节流的协议所有复杂机制都是为了实现 “可靠传输” 这个核心目标。1. 核心机制可靠传输的底层逻辑SYN (Synchronize Sequence Numbers)中文意思同步序列号作用发起连接。场景当一台计算机想要和另一台计算机建立连接时它发送的第一个包必须带有 SYN 标志。比喻相当于打电话时的“喂在吗”或者敲门声。它告诉对方“我想和你建立连接咱们来对一下暗号同步一下序列号方便后面传输数据。”ACK (Acknowledgment)中文意思确认作用回复/确认收到。场景TCP 是可靠传输所以每收到一个关键数据包都必须给对方回复一个 ACK告诉对方“我收到了”。比喻相当于对话中的“嗯”、“收到了”、“听清楚了”。如果发送方没收到 ACK它会认为你没听见就会重新发一遍。FIN (Finish)中文意思结束作用断开连接。场景当一方的数据发送完毕想要断开连接时会发送带有 FIN 标志的包。比喻相当于打电话结束时说的“我说完了”、“挂了啊”。1面向连接三次握手建立连接四次挥手断开连接TCP 通信前必须先建立双向连接通信后必须优雅断开连接这个过程是可靠传输的基础。三次握手建立连接客户端 → 服务端发送SYN包请求建立连接“你好B我想跟你建立连接。”服务端 → 客户端发送SYNACK包确认收到请求并同意建立连接“收到你的请求了ACK我也想跟你建立连接SYN。”客户端 → 服务端发送ACK包确认收到服务端的同意连接正式建立。“收到你的回复了那咱们连接建好了。”作用确保双方的收发能力都正常避免 “单边连接” 导致的数据丢失。四次挥手断开连接主动方 → 被动方发送FIN包请求断开连接“B我的数据发完了我想断开连接。”被动方 → 主动方发送ACK包确认收到断开请求“知道了ACK。但我这边可能还有点数据没发完你先等会儿。”被动方 → 主动方发送FIN包告知自己的数据已发完准备断开“好了 A我这边也都发完了现在可以彻底断开了。”主动方 → 被动方发送ACK包确认断开连接正式关闭。“好的拜拜。”作用确保双方的数据都传输完毕避免断开时丢包。2可靠传输序列号 确认应答 重传机制这是 TCP 最核心的 “可靠性保障”解决了丢包、重复、乱序三大问题序列号每个字节的数据都有唯一的序列号接收方可以通过序列号判断数据是否乱序、是否重复。确认应答ACK接收方收到数据后会发送一个ACK包告诉发送方 “我已经收到了序列号 X 之前的所有数据”。重传机制如果发送方在超时时间内没收到ACK就认为数据丢了会自动重传超时重传等待超时后重传通用场景快速重传如果收到 3 个重复的ACK直接重传不用等超时效率更高。3流量控制 拥塞控制避免网络过载流量控制通过滑动窗口实现接收方会告诉发送方 “我当前能接收的最大数据量”发送方不会超过这个量避免接收方处理不过来导致丢包。拥塞控制TCP 会根据网络状况动态调整发送速率比如慢启动、拥塞避免、快速恢复防止大量数据涌入网络导致拥堵。4基于字节流无数据边界可能出现粘包TCP 把数据当成连续的字节流传输没有 “数据包边界” 的概念。比如发送方分 3 次发A、B、C接收方可能一次收到ABC也可能分两次收到ABC—— 这就是粘包问题需要应用层自己定义 “分隔符” 或 “长度字段” 来解决。2. TCP 头部结构开销大TCP 头部至少 20 字节最多 60 字节可选字段包含序列号、确认号、窗口大小、标志位SYN/ACK/FIN 等等关键信息 —— 这些字段是实现可靠传输的基础但也带来了额外的网络开销。三、UDP 详解无连接的高效传输协议UDP 是无连接、不可靠、基于数据报的协议完全抛弃了 TCP 的复杂机制只保留最核心的 “发送数据” 功能。1. 核心特性高效传输的底层逻辑1无连接无需握手直接发送UDP 通信前不需要建立连接发送方不管接收方是否在线、是否准备好直接把数据封装成 “数据报” 发出去通信后也不需要断开连接。优点省去了三次握手 / 四次挥手的时间延迟极低缺点无法确认接收方是否存在数据丢了也不知道。2不可靠传输无确认、无重传、无排序UDP 没有序列号、ACK 包、重传机制数据一旦发出去就 “生死由命”数据可能丢包网络拥堵时路由器会优先丢弃 UDP 包数据可能乱序后发的包可能先到接收方不会自动排序数据可能重复同一个包可能被多次接收。3基于数据报有边界不会粘包UDP 把数据当成独立的数据包传输每个数据报都有明确的边界发送方发一个 100 字节的包接收方要么完整收到 100 字节要么完全收不到 ——不会出现粘包问题应用层处理更简单。4支持广播 / 组播一对多传输UDP 支持广播发给同一网段的所有主机和组播发给特定组的主机而 TCP 只能一对一传输。这一特性让 UDP 很适合游戏的 “房间广播”“服务器公告” 等场景。2. UDP 头部结构开销极小UDP 头部只有 8 字节只包含 4 个字段源端口、目的端口、数据长度、校验和 —— 没有任何额外控制字段传输效率极高。四、TCP 与 UDP 核心区别详细对比表对比维度TCPUDP关键差异解读连接性面向连接无连接TCP 必须三次握手建立连接UDP 直接发数据延迟更低可靠性可靠传输不可靠传输TCP 保证数据不丢、不重、有序UDP 不保证丢包不重传传输单位字节流无边界数据报有边界TCP 可能粘包需应用层处理UDP 不会粘包直接按包接收头部开销20~60 字节8 字节TCP 头部字段多开销大UDP 极简开销小确认机制确认应答ACK无确认应答TCP 收到数据必回 ACKUDP 发完不管重传机制超时重传 快速重传无重传机制TCP 丢包会重传UDP 丢包就丢了流量控制支持滑动窗口不支持TCP 会根据接收方能力调整速率UDP 无脑发拥塞控制支持慢启动等不支持TCP 会根据网络状况降速UDP 可能加剧网络拥堵通信方式一对一一对一 / 一对多广播 / 组播UDP 适合群发TCP 只能点对点延迟 / 效率延迟高效率低延迟低效率高TCP 机制复杂UDP 极简实时性更好五、适用场景结合游戏开发重点说明TCP 和 UDP 没有绝对的优劣只有场景适配性游戏开发中通常会两者混用。1. TCP 的适用场景数据不能丢准确性优先适合低频、关键、对实时性要求不高的数据传输比如游戏登录验证、账号密码传输、角色注册道具交易、金币充值、存档同步这些数据丢了会导致用户权益受损游戏公告、邮件推送不需要实时到达但必须准确到达。2. UDP 的适用场景实时性优先允许少量丢包适合高频、实时、对可靠性要求不高的数据传输比如多人游戏的角色移动、视角转动、技能释放哪怕丢 1-2 个包后续的新位置包也能补上不会影响体验实时语音聊天、游戏内的实时弹幕房间内的玩家状态广播比如 “玩家 A 进入房间”。3. 游戏开发的最佳实践TCP UDP 混用几乎所有大型多人在线游戏MMO都采用这种模式用UDP 处理实时性强的高频数据角色移动、战斗同步保证游戏流畅用TCP 处理关键的低频数据登录、交易、存档保证数据准确基于 UDP 自己实现 “轻量级可靠性”比如对关键的 UDP 包如技能释放指令手动加确认和重传机制兼顾实时性和可靠性虚幻引擎的NetDriver就有类似设计。六、案例UDP:Server:Linux//UdpServer.cc #include iostream #include string #include memory #include cerrno #include cstring #include unistd.h #include strings.h #include sys/types.h #include sys/socket.h #include netinet/in.h #include arpa/inet.h const static uint16_t defaultport 8888; const static int defaultfd -1; const static int defaultsize 1024; enum { Usage_Err 1, Socket_Err, Bind_Err }; class UdpServer { public: UdpServer(uint16_t port defaultport) : _port(port), _sockfd(defaultfd) { } void Init() { // 1. 创建socket就是创建了文件细节 _sockfd socket(AF_INET, SOCK_DGRAM, 0); if (_sockfd 0) { exit(Socket_Err); } // 2. 绑定指定网络信息 struct sockaddr_in local; bzero(local, sizeof(local)); // memset local.sin_family AF_INET; local.sin_port htons(_port); local.sin_addr.s_addr INADDR_ANY; // 1. 4字节IP 2. 变成网络序列 // 结构体填完设置到内核中了吗没有 int n ::bind(_sockfd, (struct sockaddr *)local, sizeof(local)); if (n ! 0) { exit(Bind_Err); } } void Start() { // 服务器永远不退出 char buffer[defaultsize]; for (;;) { struct sockaddr_in peer; socklen_t len sizeof(peer); // 不能乱写 ssize_t n recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)peer, len); if (n 0) { uint16_t clientport ntohs(peer.sin_port); std::string clientip inet_ntoa(peer.sin_addr); std::string prefix clientip : std::to_string(clientport); buffer[n] 0; std::cout prefix # buffer std::endl; std::string echo buffer; echo [udp server echo message]; sendto(_sockfd, echo.c_str(), echo.size(), 0, (struct sockaddr *)peer, len); } } } ~UdpServer() { } private: uint16_t _port; int _sockfd; }; void Usage(std::string proc) { std::cout Usage : \n\t proc local_port\n std::endl; } // ./udp_server 8888 int main(int argc, char *argv[]) { if (argc ! 2) { Usage(argv[0]); return Usage_Err; } uint16_t port std::stoi(argv[1]); std::unique_ptrUdpServer usvr std::make_uniqueUdpServer(port); usvr-Init(); usvr-Start(); return 0; }Client:Windows//UdpClient.cc #include iostream #include cstdio #include thread #include string #include cstdlib #include WinSock2.h #include Windows.h #pragma warning(disable : 4996) #pragma comment(lib, ws2_32.lib) std::string serverip ; // 填写你的云服务器ip uint16_t serverport 8888; // 填写你的云服务开放的端口号 int main() { WSADATA wsd; WSAStartup(MAKEWORD(2, 2), wsd); struct sockaddr_in server; memset(server, 0, sizeof(server)); server.sin_family AF_INET; server.sin_port htons(serverport); //? server.sin_addr.s_addr inet_addr(serverip.c_str()); SOCKET sockfd socket(AF_INET, SOCK_DGRAM, 0); if (sockfd SOCKET_ERROR) { std::cout socker error std::endl; return 1; } std::string message; char buffer[1024]; while (true) { std::cout Please Enter ; std::getline(std::cin, message); if(message.empty()) continue; sendto(sockfd, message.c_str(), (int)message.size(), 0, (struct sockaddr *)server, sizeof(server)); struct sockaddr_in temp; int len sizeof(temp); int s recvfrom(sockfd, buffer, 1023, 0, (struct sockaddr *)temp, len); if (s 0) { buffer[s] 0; std::cout buffer std::endl; } } closesocket(sockfd); WSACleanup(); return 0; }TCP:Sever:Linux//TcpClient.cc #include winsock2.h #include iostream #include string #pragma warning(disable : 4996) #pragma comment(lib, ws2_32.lib) std::string serverip ; // 填写你的云服务器ip uint16_t serverport 8888; // 填写你的云服务开放的端口号 int main() { WSADATA wsaData; int result WSAStartup(MAKEWORD(2, 2), wsaData); if (result ! 0) { std::cerr WSAStartup failed: result std::endl; return 1; } SOCKET clientSocket socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (clientSocket INVALID_SOCKET) { std::cerr socket failed std::endl; WSACleanup(); return 1; } sockaddr_in serverAddr; serverAddr.sin_family AF_INET; serverAddr.sin_port htons(serverport); // 替换为服务器端口 serverAddr.sin_addr.s_addr inet_addr(serverip.c_str()); // 替换为服务器IP地址 result connect(clientSocket, (SOCKADDR *)serverAddr, sizeof(serverAddr)); if (result SOCKET_ERROR) { std::cerr connect failed std::endl; closesocket(clientSocket); WSACleanup(); return 1; } while (true) { std::string message; std::cout Please Enter ; std::getline(std::cin, message); if(message.empty()) continue; send(clientSocket, message.c_str(), message.size(), 0); char buffer[1024] {0}; int bytesReceived recv(clientSocket, buffer, sizeof(buffer) - 1, 0); if (bytesReceived 0) { buffer[bytesReceived] \0; // 确保字符串以 null 结尾 std::cout Received from server: buffer std::endl; } else { std::cerr recv failed std::endl; } } closesocket(clientSocket); WSACleanup(); return 0; }Client:Windows//TcpServer.cc #include iostream #include string #include cerrno #include cstring #include cstdlib #include memory #include sys/types.h #include sys/socket.h #include netinet/in.h #include arpa/inet.h #include sys/wait.h #include unistd.h const static int default_backlog 6; enum { Usage_Err 1, Socket_Err, Bind_Err, Listen_Err }; #define CONV(addr_ptr) ((struct sockaddr *)addr_ptr) class TcpServer { public: TcpServer(uint16_t port) : _port(port), _isrunning(false) { } // 都是固定套路 void Init() { // 1. 创建socket, file fd, 本质是文件 _listensock socket(AF_INET, SOCK_STREAM, 0); if (_listensock 0) { exit(0); } int opt 1; setsockopt(_listensock, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, opt, sizeof(opt)); // 2. 填充本地网络信息并bind struct sockaddr_in local; memset(local, 0, sizeof(local)); local.sin_family AF_INET; local.sin_port htons(_port); local.sin_addr.s_addr htonl(INADDR_ANY); // 2.1 bind if (bind(_listensock, CONV(local), sizeof(local)) ! 0) { exit(Bind_Err); } // 3. 设置socket为监听状态tcp特有的 if (listen(_listensock, default_backlog) ! 0) { exit(Listen_Err); } } void ProcessConnection(int sockfd, struct sockaddr_in peer) { uint16_t clientport ntohs(peer.sin_port); std::string clientip inet_ntoa(peer.sin_addr); std::string prefix clientip : std::to_string(clientport); std::cout get a new connection, info is : prefix std::endl; while (true) { char inbuffer[1024]; ssize_t s ::read(sockfd, inbuffer, sizeof(inbuffer)-1); if(s 0) { inbuffer[s] 0; std::cout prefix # inbuffer std::endl; std::string echo inbuffer; echo [tcp server echo message]; write(sockfd, echo.c_str(), echo.size()); } else { std::cout prefix client quit std::endl; break; } } } void Start() { _isrunning true; while (_isrunning) { // 4. 获取连接 struct sockaddr_in peer; socklen_t len sizeof(peer); int sockfd accept(_listensock, CONV(peer), len); if (sockfd 0) { continue; } ProcessConnection(sockfd, peer); } } ~TcpServer() { } private: uint16_t _port; int _listensock; // TODO bool _isrunning; }; using namespace std; void Usage(std::string proc) { std::cout Usage : \n\t proc local_port\n std::endl; } // ./tcp_server 8888 int main(int argc, char *argv[]) { if (argc ! 2) { Usage(argv[0]); return Usage_Err; } uint16_t port stoi(argv[1]); std::unique_ptrTcpServer tsvr make_uniqueTcpServer(port); tsvr-Init(); tsvr-Start(); return 0; }
延伸阅读

更多相关文章

2026/9/20 0:25:01

TestDisk与PhotoRec:数据恢复的终极免费解决方案

TestDisk与PhotoRec:数据恢复的终极免费解决方案 【免费下载链接】testdisk TestDisk & PhotoRec 项目地址: https://gitcode.com/gh_mirrors/te/testdisk 当你的硬盘分区神秘消失,或者重要文件被意外删除时,那种恐慌感是每个电脑…

2026/9/21 4:48:15

如何免费解锁全网无损音乐:洛雪音乐音源终极配置指南

如何免费解锁全网无损音乐:洛雪音乐音源终极配置指南 【免费下载链接】lxmusic- lxmusic(洛雪音乐)全网最新最全音源 项目地址: https://gitcode.com/gh_mirrors/lx/lxmusic- 想要免费享受酷我、酷狗、QQ音乐、网易云等平台的FLAC无损音乐吗?洛雪…

2026/9/20 0:25:02

破解ESP32 Arduino开发中库依赖冲突的终极方案

破解ESP32 Arduino开发中库依赖冲突的终极方案 【免费下载链接】arduino-esp32 Arduino core for the ESP32 family of SoCs 项目地址: https://gitcode.com/GitHub_Trending/ar/arduino-esp32 当你在PlatformIO环境中进行ESP32 Arduino开发时,是否经常遇到&…

2026/9/23 1:12:22

GPT2中文模型深度部署:从源码级定制到生产优化

简介:本资源是一份面向NLP初学者与进阶开发者的Python实践项目,聚焦GPT-2模型在中文文本生成任务中的完整实现路径,涵盖数据预处理、模型微调、对话生成及评估部署等核心环节。压缩包共16个文件(9个Python脚本承担训练、生成、数据…

2026/9/23 1:12:22

Kilo Code:统一CLI抽象层重构AI开发工作流

1. 这不是又一个“AI插件合集”,而是CLI工作流的底层重构你有没有过这种体验:在VS Code里装了七八个AI相关插件——Codex CLI、Claude Code、Trae CLI、Wokwi for VS Code,甚至还有自己编译的Deveco CLI二进制;每次打开编辑器&…

2026/9/23 1:12:22

平板电脑如何强制开机完整示例:3个底层原理坑点与实战排查指南

平板电脑如何强制开机完整示例:3个底层原理坑点与实战排查指南 面试被问“设备无响应时如何强制重启”,90%的开发者只能背出“长按电源键”,却说不清底层电源管理单元(PMU)是如何响应中断的。这不仅仅是运维操作,更是嵌入式系统与硬件交互的核心…

2026/9/23 1:07:22

基于种群进化算法的数字化车间排产调度系统实现解析

简介:面向数字化车间智能排产调度挑战赛的Python源码项目,围绕工业4.0背景下生产过程数字化与智能优化的实际需求,整合了从数据处理、算法设计到结果展示的完整赛题方案,适合智能制造、运筹优化方向的开发者与参赛者学习。压缩包共…

2026/9/22 10:02:42

GAMP 5 基于风险的计算机化系统验证:软件分类与审计追踪实践

简介:《A Risk-Based Approach to Compliant GxP Computerized Systems》即业内熟知的GAMP 5指南,面向制药企业质量与IT合规人员、验证工程师及计算机化系统管理者,用于解决GxP法规环境下系统合规性难以科学落地的问题。文档以风险管理为主线…

2026/9/22 9:07:39

安全托管MSSP实战:从静态防御到人机协同的攻防运营与应急响应

简介:这份PPT围绕互联网业务安全托管服务展开,面向企业安全负责人、IT运维人员及关注MSSP/MSS选型的读者,重点回应传统安全过度依赖人工、碎片化静态防御难以对抗产业化攻击等痛点。资源共1个pptx文件,包体约30.63MB,以…

2026/9/23 0:01:54

3个实战技巧搞定形式英语:从看教程到跑通性能优化

3个实战技巧搞定形式英语:从看教程到跑通性能优化 看了一堆教程还是不会写项目?别慌,这种“眼高手低”的困境在开发者圈子里太常见了。很多人以为卡点在语法,其实真正拦路虎是缺乏将知识点串联成完整链路的能力。今天咱们不聊虚的,直接拿【形式英语】这…

2026/9/22 16:34:32

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/22 20:01:30

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/22 13:25:41

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码