很多同学刚学网络编程时,都会有一个疑问:既然 UDP 会丢包、不可靠,那它还有什么用?为什么语音、视频、会议、屏幕共享这些场景,反而经常用 UDP?这篇文章就把UDP 的应用场景、优缺点、与 TCP 的本质区别讲清楚,并且通过详细代码案例 + 运行结果来直观演示:UDP 为什么高效UDP 为什么可能丢数据什么业务适合 UDP,什么业务必须 TCP大公司为什么还敢在重要场景里使用 UDP如何在应用层“补足” UDP 的可靠性一、先说结论:UDP 不是“差”,而是“取舍不同”很多人一听 UDP 的特点:无连接不可靠可能丢包可能乱序第一反应是:这协议是不是很弱?其实不是。UDP 的核心思想不是“保证万无一失”,而是:尽快发出去,少做事,把延迟降到更低。也就是说,UDP 并不是“低级版 TCP”,它只是服务于另一类业务需求。二、UDP 的核心特性先回顾一下 UDP 的几个关键标签。1. 面向无连接UDP 发送数据前不需要像 TCP 一样先建立连接。也就是说,没有:三次握手四次挥手连接状态维护所以它的流程很简单:发送方:socket() - sendto() 接收方:socket() - bind() - recvfrom()2. 面向报文UDP 是按“数据报”发送的。你发 100 字节,对方就按一个完整数据报接收。不会像 TCP 那样把数据流拆成“没有边界”的字节流来处理。3. 不可靠,可能丢包UDP 不保证:一定送达一定按顺序到达一定不会重复一定不会丢失发送方发完就算“完成任务”,至于对方到底收到没收到,UDP 默认不关心。4. 效率高因为 UDP 比 TCP 少做了很多事情:不建立连接不维护连接状态没有确认应答 ACK没有重传机制没有滑动窗口没有复杂的拥塞控制所以它天然具有:更低延迟更小协议开销更高实时性三、图解:UDP 与 TCP 的本质差别下面用一张简单图来理解。TCP: 客户端 服务器 | ---- SYN ------- | | --- SYN+ACK ---- | | ---- ACK ------- | | ===== 传输数据 ===== | | ---- FIN ------- | | --- ACK -------- | | --- FIN -------- | | ---- ACK ------- | UDP: 客户端 服务器 | ---- 数据报 ----- | | ---- 数据报 ----- | | ---- 数据报 ----- |结论TCP 更像“打电话”:先接通再说话还要确认对方是否听到UDP 更像“喊话”:不打招呼,直接发发出去就算了对方收没收到,默认不确认四、UDP 适合什么场景?这一部分是课堂内容的核心。1. 适合:对“少量丢失”不敏感的场景UDP 最适合这样的业务:实时性比完整性更重要比如:语音通话视频会议屏幕共享在线直播局域网广播发现实时游戏状态同步因为这些场景里,用户通常能容忍“少量缺失”,但不能接受“严重延迟”。2. 不适合:对数据完整性要求极高的场景用户名密码传输用户名、密码属于高度敏感数据。如果丢了一部分:认证失败登录异常业务逻辑错误所以这类数据传输必须优先使用TCP或基于 TCP 的上层协议。文件传输文件更是典型的“不允许丢一个字节”。比如一张图片:少一个字节,都可能损坏压缩包可能无法解压程序文件可能无法执行所以文件传输也必须优先使用TCP。五、典型应用场景分析下面结合课堂上的几个例子详细展开。场景 1:语音通信比如:微信语音QQ 语音会议语音为什么可以用 UDP?因为语音本身就是连续流式数据。如果某一瞬间丢了几个字、一个音节,通常用户可以接受。图示理解正常语音: 你好,今天晚上一起吃饭吗? 轻微丢包后: 你好,今天晚...一起吃饭吗? 用户通常仍能理解意思。如果此时为了等丢失的数据“重传”回来再播放,那反而会导致:声音卡顿延迟明显对话体验变差所以语音更重视:及时到达,而不是百分百完整场景 2:视频聊天 / 视频会议比如:QQ 视频微信视频腾讯会议在线课堂视频视频画面本质上是一帧一帧不断刷新的。假设某一帧丢了一小部分数据,可能会出现:某个角落短暂模糊某一帧轻微马赛克个别帧缺失但下一帧很快就刷新出来了。图示理解第100帧:正常 第101帧:右下角少一块 第102帧:恢复正常用户大多数时候不会明显察觉。所以视频场景也是典型的 UDP 使用场景。场景 3:屏幕共享课堂中提到的一个例子非常经典:远程教学、屏幕共享。可以理解为:教师端不断截图把截图切分成多块数据通过网络发给学生端学生端收到后重新拼接显示示意图一张屏幕截图 +----+----+----+----+ | 1 | 2 | 3 | 4 | +----+----+----+----+ | 5 | 6 | 7 | 8 | +----+----+----+----+ | 9 | 10 | 11 | 12 | +----+----+----+----+如果传输过程中第 7 块丢了,会发生什么?+----+----+----+----+ | 1 | 2 | 3 | 4 | +----+----+----+----+ | 5 | 6 | XX | 8 | +----+----+----+----+ | 9 | 10 | 11 | 12 | +----+----+----+----+可能画面有一小块暂时没显示出来。但因为下一帧马上又来了,这个问题很快就被“覆盖”。所以屏幕共享通常也能接受 UDP 的轻微丢包。六、为什么 UDP 一般更适合局域网课堂笔记中提到一个非常重要的点:UDP 一般更适合局域网环境。原因很简单:局域网网络质量通常更稳定丢包率较低时延更小抖动更小而在广域网(公网)里,网络环境复杂:链路长设备多干扰大拥塞更容易发生这时 UDP 的问题就更明显:丢包更多乱序更明显体验不稳定所以很多实时应用在广域网使用 UDP 时,都会额外做很多应用层优化。七、代码实验 1:UDP 基础通信先写一个最简单的 UDP 服务器和客户端,感受它“无连接、简单直接”的特点。1. 服务器代码udp_server.c#includestdio.h#includestdlib.h#includestring.h#includeunistd.h#includearpa/inet.h#definePORT8989#defineBUF_SIZE1024intmain(){intfd=socket(AF_INET,SOCK_DGRAM,0);if(fd==-1){perror("socket");return1;}structsockaddr_inservaddr,cliaddr;socklen_tlen=sizeof(cliaddr);charbuf[BUF_SIZE];memset(servaddr,0,sizeof(servaddr));servaddr.sin_family=AF_INET;servaddr.sin_port=htons(PORT);servaddr.sin_addr.s_addr=htonl(INADDR_ANY);if(bind(fd,(structsockaddr*)servaddr,sizeof(servaddr))==-1){perror("bind");close(fd);return1;}printf("UDP server started, port=%d\n",PORT);while(1){memset(buf,0,sizeof(buf));intn=recvfrom(fd,buf,sizeof(buf),0,(structsockaddr*)cliaddr,len);if(n==-1){perror("recvfrom");break;}charip[16];inet_ntop(AF_INET,cliaddr.sin_addr.s_addr,ip,sizeof(ip));printf("[server] recv from %s:%d - %s\n",ip,ntohs(cliaddr.sin_port),buf);sendto(fd,buf,strlen(buf)+1,0,(structsockaddr*)cliaddr,len);}close(fd);return0;}2. 客户端代码udp_client.c#includestdio.h
UDP 适合用在什么场景?一文讲透应用场景、代码实验与 TCP 对比
很多同学刚学网络编程时,都会有一个疑问:既然 UDP 会丢包、不可靠,那它还有什么用?为什么语音、视频、会议、屏幕共享这些场景,反而经常用 UDP?这篇文章就把UDP 的应用场景、优缺点、与 TCP 的本质区别讲清楚,并且通过详细代码案例 + 运行结果来直观演示:UDP 为什么高效UDP 为什么可能丢数据什么业务适合 UDP,什么业务必须 TCP大公司为什么还敢在重要场景里使用 UDP如何在应用层“补足” UDP 的可靠性一、先说结论:UDP 不是“差”,而是“取舍不同”很多人一听 UDP 的特点:无连接不可靠可能丢包可能乱序第一反应是:这协议是不是很弱?其实不是。UDP 的核心思想不是“保证万无一失”,而是:尽快发出去,少做事,把延迟降到更低。也就是说,UDP 并不是“低级版 TCP”,它只是服务于另一类业务需求。二、UDP 的核心特性先回顾一下 UDP 的几个关键标签。1. 面向无连接UDP 发送数据前不需要像 TCP 一样先建立连接。也就是说,没有:三次握手四次挥手连接状态维护所以它的流程很简单:发送方:socket() - sendto() 接收方:socket() - bind() - recvfrom()2. 面向报文UDP 是按“数据报”发送的。你发 100 字节,对方就按一个完整数据报接收。不会像 TCP 那样把数据流拆成“没有边界”的字节流来处理。3. 不可靠,可能丢包UDP 不保证:一定送达一定按顺序到达一定不会重复一定不会丢失发送方发完就算“完成任务”,至于对方到底收到没收到,UDP 默认不关心。4. 效率高因为 UDP 比 TCP 少做了很多事情:不建立连接不维护连接状态没有确认应答 ACK没有重传机制没有滑动窗口没有复杂的拥塞控制所以它天然具有:更低延迟更小协议开销更高实时性三、图解:UDP 与 TCP 的本质差别下面用一张简单图来理解。TCP: 客户端 服务器 | ---- SYN ------- | | --- SYN+ACK ---- | | ---- ACK ------- | | ===== 传输数据 ===== | | ---- FIN ------- | | --- ACK -------- | | --- FIN -------- | | ---- ACK ------- | UDP: 客户端 服务器 | ---- 数据报 ----- | | ---- 数据报 ----- | | ---- 数据报 ----- |结论TCP 更像“打电话”:先接通再说话还要确认对方是否听到UDP 更像“喊话”:不打招呼,直接发发出去就算了对方收没收到,默认不确认四、UDP 适合什么场景?这一部分是课堂内容的核心。1. 适合:对“少量丢失”不敏感的场景UDP 最适合这样的业务:实时性比完整性更重要比如:语音通话视频会议屏幕共享在线直播局域网广播发现实时游戏状态同步因为这些场景里,用户通常能容忍“少量缺失”,但不能接受“严重延迟”。2. 不适合:对数据完整性要求极高的场景用户名密码传输用户名、密码属于高度敏感数据。如果丢了一部分:认证失败登录异常业务逻辑错误所以这类数据传输必须优先使用TCP或基于 TCP 的上层协议。文件传输文件更是典型的“不允许丢一个字节”。比如一张图片:少一个字节,都可能损坏压缩包可能无法解压程序文件可能无法执行所以文件传输也必须优先使用TCP。五、典型应用场景分析下面结合课堂上的几个例子详细展开。场景 1:语音通信比如:微信语音QQ 语音会议语音为什么可以用 UDP?因为语音本身就是连续流式数据。如果某一瞬间丢了几个字、一个音节,通常用户可以接受。图示理解正常语音: 你好,今天晚上一起吃饭吗? 轻微丢包后: 你好,今天晚...一起吃饭吗? 用户通常仍能理解意思。如果此时为了等丢失的数据“重传”回来再播放,那反而会导致:声音卡顿延迟明显对话体验变差所以语音更重视:及时到达,而不是百分百完整场景 2:视频聊天 / 视频会议比如:QQ 视频微信视频腾讯会议在线课堂视频视频画面本质上是一帧一帧不断刷新的。假设某一帧丢了一小部分数据,可能会出现:某个角落短暂模糊某一帧轻微马赛克个别帧缺失但下一帧很快就刷新出来了。图示理解第100帧:正常 第101帧:右下角少一块 第102帧:恢复正常用户大多数时候不会明显察觉。所以视频场景也是典型的 UDP 使用场景。场景 3:屏幕共享课堂中提到的一个例子非常经典:远程教学、屏幕共享。可以理解为:教师端不断截图把截图切分成多块数据通过网络发给学生端学生端收到后重新拼接显示示意图一张屏幕截图 +----+----+----+----+ | 1 | 2 | 3 | 4 | +----+----+----+----+ | 5 | 6 | 7 | 8 | +----+----+----+----+ | 9 | 10 | 11 | 12 | +----+----+----+----+如果传输过程中第 7 块丢了,会发生什么?+----+----+----+----+ | 1 | 2 | 3 | 4 | +----+----+----+----+ | 5 | 6 | XX | 8 | +----+----+----+----+ | 9 | 10 | 11 | 12 | +----+----+----+----+可能画面有一小块暂时没显示出来。但因为下一帧马上又来了,这个问题很快就被“覆盖”。所以屏幕共享通常也能接受 UDP 的轻微丢包。六、为什么 UDP 一般更适合局域网课堂笔记中提到一个非常重要的点:UDP 一般更适合局域网环境。原因很简单:局域网网络质量通常更稳定丢包率较低时延更小抖动更小而在广域网(公网)里,网络环境复杂:链路长设备多干扰大拥塞更容易发生这时 UDP 的问题就更明显:丢包更多乱序更明显体验不稳定所以很多实时应用在广域网使用 UDP 时,都会额外做很多应用层优化。七、代码实验 1:UDP 基础通信先写一个最简单的 UDP 服务器和客户端,感受它“无连接、简单直接”的特点。1. 服务器代码udp_server.c#includestdio.h#includestdlib.h#includestring.h#includeunistd.h#includearpa/inet.h#definePORT8989#defineBUF_SIZE1024intmain(){intfd=socket(AF_INET,SOCK_DGRAM,0);if(fd==-1){perror("socket");return1;}structsockaddr_inservaddr,cliaddr;socklen_tlen=sizeof(cliaddr);charbuf[BUF_SIZE];memset(servaddr,0,sizeof(servaddr));servaddr.sin_family=AF_INET;servaddr.sin_port=htons(PORT);servaddr.sin_addr.s_addr=htonl(INADDR_ANY);if(bind(fd,(structsockaddr*)servaddr,sizeof(servaddr))==-1){perror("bind");close(fd);return1;}printf("UDP server started, port=%d\n",PORT);while(1){memset(buf,0,sizeof(buf));intn=recvfrom(fd,buf,sizeof(buf),0,(structsockaddr*)cliaddr,len);if(n==-1){perror("recvfrom");break;}charip[16];inet_ntop(AF_INET,cliaddr.sin_addr.s_addr,ip,sizeof(ip));printf("[server] recv from %s:%d - %s\n",ip,ntohs(cliaddr.sin_port),buf);sendto(fd,buf,strlen(buf)+1,0,(structsockaddr*)cliaddr,len);}close(fd);return0;}2. 客户端代码udp_client.c#includestdio.h