当前位置: 首页 > news >正文

linux学习笔记(32)网络编程——UDP

UDP是什么?

UDP = 像寄明信片
  • 写了就寄,不管对方收没收到
  • 不保证顺序,可能先寄的后到
  • 简单快速,没有复杂流程

1. UDP vs TCP 直观对比

TCP(像打电话):

// 必须建立连接
connect() → 三次握手 → 可靠传输 → 四次挥手
// 保证:顺序、不丢失、不重复

UDP(像寄明信片):

// 直接发送,无连接
sendto() → 网络 → 可能到达,可能丢失
// 不保证:顺序、不丢失、不重复

2. 最简单的UDP服务器

#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() {printf("=== UDP服务器启动 ===\n");// 1. 创建UDP socketint sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0) {perror("socket创建失败");exit(1);}printf("1. UDP socket创建成功\n");// 2. 设置服务器地址struct sockaddr_in server_addr;server_addr.sin_family = AF_INET;server_addr.sin_addr.s_addr = INADDR_ANY;  // 监听所有网卡server_addr.sin_port = htons(PORT);        // 端口号// 3. 绑定地址(UDP也需要bind!)if (bind(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {perror("绑定失败");close(sockfd);exit(1);}printf("2. 绑定端口 %d 成功\n", PORT);printf("3. 等待客户端数据...\n\n");// 主循环while (1) {char buffer[BUFFER_SIZE];struct sockaddr_in client_addr;socklen_t client_len = sizeof(client_addr);// 4. 接收数据(阻塞等待)int bytes_received = recvfrom(sockfd, buffer, sizeof(buffer) - 1, 0,(struct sockaddr*)&client_addr, &client_len);if (bytes_received > 0) {buffer[bytes_received] = '\0';  // 添加字符串结束符// 获取客户端信息char client_ip[INET_ADDRSTRLEN];inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, sizeof(client_ip));int client_port = ntohs(client_addr.sin_port);printf("📨 收到来自 %s:%d 的消息:\n", client_ip, client_port);printf("   内容: %s\n", buffer);printf("   长度: %d 字节\n\n", bytes_received);// 5. 回复客户端char response[BUFFER_SIZE];snprintf(response, sizeof(response), "服务器已收到你的消息: %s", buffer);sendto(sockfd, response, strlen(response), 0,(struct sockaddr*)&client_addr, client_len);printf("📤 已发送回复\n\n");}}close(sockfd);return 0;
}

3. 最简单的UDP客户端

#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 PORT 8080
#define BUFFER_SIZE 1024int main() {printf("=== UDP客户端启动 ===\n");// 1. 创建UDP socketint sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0) {perror("socket创建失败");exit(1);}printf("1. UDP socket创建成功\n");// 2. 设置服务器地址struct sockaddr_in server_addr;server_addr.sin_family = AF_INET;server_addr.sin_port = htons(PORT);inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr);printf("2. 目标服务器: %s:%d\n", SERVER_IP, PORT);while (1) {char buffer[BUFFER_SIZE];// 3. 获取用户输入printf("\n💬 请输入要发送的消息 (输入quit退出): ");fgets(buffer, sizeof(buffer), stdin);buffer[strcspn(buffer, "\n")] = '\0';  // 去掉换行符if (strcmp(buffer, "quit") == 0) {break;}// 4. 发送数据到服务器int bytes_sent = sendto(sockfd, buffer, strlen(buffer), 0,(struct sockaddr*)&server_addr, sizeof(server_addr));printf("📤 发送: %s (%d字节)\n", buffer, bytes_sent);// 5. 接收服务器回复char response[BUFFER_SIZE];struct sockaddr_in from_addr;socklen_t from_len = sizeof(from_addr);int bytes_received = recvfrom(sockfd, response, sizeof(response) - 1, 0,(struct sockaddr*)&from_addr, &from_len);if (bytes_received > 0) {response[bytes_received] = '\0';printf("📥 收到回复: %s\n", response);} else {printf("❌ 未收到回复 (可能丢失)\n");}}close(sockfd);printf("👋 客户端退出\n");return 0;
}

4. 编译和测试

编译命令:

# 编译UDP服务器
gcc -o udp_server udp_server.c
# 编译UDP客户端
gcc -o udp_client udp_client.c

测试步骤:

  1. 终端1 - 启动UDP服务器:
./udp_server
  1. 终端2 - 启动UDP客户端:
./udp_client
输入:Hello UDP!
  1. 终端3 - 再启动一个UDP客户端:
./udp_client
输入:This is client 2

预期输出:

服务器输出:
=== UDP服务器启动 ===
1. UDP socket创建成功
2. 绑定端口 8080 成功
3. 等待客户端数据...📨 收到来自 127.0.0.1:54321 的消息:内容: Hello UDP!长度: 10 字节📤 已发送回复📨 收到来自 127.0.0.1:54322 的消息:内容: This is client 2长度: 16 字节📤 已发送回复

客户端输出:
=== UDP客户端启动 ===
1. UDP socket创建成功
2. 目标服务器: 127.0.0.1:8080💬 请输入要发送的消息 (输入quit退出): Hello UDP!
📤 发送: Hello UDP! (10字节)
📥 收到回复: 服务器已收到你的消息: Hello UDP!💬 请输入要发送的消息 (输入quit退出): quit
👋 客户端退出

5. UDP的核心特点

无连接:

// 不需要connect(),直接发送
sendto(sockfd, data, len, 0, &server_addr, addr_len);// 每个数据包都是独立的
// 可以同时给多个服务器发送数据

不可靠:

// 数据可能:
// - 丢失(网络拥堵)
// - 重复(网络重传)
// - 乱序(走不同路径)
// - 损坏(比特错误)

简单高效:

// 头部只有8字节
struct udp_header {uint16_t source_port;uint16_t dest_port;uint16_t length;uint16_t checksum;
};// 相比TCP的20字节头部,开销小很多

6. UDP的关键函数

sendto() - 发送数据

int sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);

recvfrom() - 接收数据

int recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
// src_addr会填充发送者的地址信息

7. UDP的使用场景

适合UDP的场景:

// 1. 实时音视频通话
//    - 丢失几帧没关系,但要低延迟// 2. DNS查询
//    - 简单请求响应,丢了重试就行// 3. 游戏数据包
//    - 位置更新,丢了用新的覆盖// 4. 广播/组播
//    - 一对多通信

不适合UDP的场景:

// 1. 文件传输
//    - 不能丢失任何数据// 2. 网页浏览
//    - 需要保证所有数据正确到达// 3. 电子邮件
//    - 必须可靠传输

8. UDP的优缺点总结

优点:

  • ✅ 速度快:没有连接建立、确认、重传
  • ✅ 开销小:头部只有8字节
  • ✅ 无连接:可以同时与多个对端通信
  • ✅ 适合广播:天然支持一对多

缺点:

  • ❌ 不可靠:可能丢失、重复、乱序
  • ❌ 无拥塞控制:可能加剧网络拥堵
  • ❌ 需要应用层处理:自己实现可靠性

🎯 终极对比:

特性
TCP (打电话)
UDP (寄明信片)
连接
需要建立连接
无连接
可靠性
保证不丢失
可能丢失
顺序
保证顺序
不保证顺序
速度
头部
20字节
8字节
控制
有流量和拥塞控制
无控制
同一个端口,可以被tcp和udp同时使用
一个进程可以创建很多个套接字,也可以使用两个以上的端口
http://www.dtcms.com/a/482789.html

相关文章:

  • 2025全新三防平板科普:5G-A+卫星通信+国产化
  • 电商网站建设懂你所需wordpress一句话木马
  • 「机器学习笔记14」集成学习全面解析:从Bagging到Boosting的Python实战指南
  • 小迪安全v2023学习笔记(一百三十一讲)—— Web权限提升篇划分获取资产服务后台系统数据库管理相互转移
  • Java高并发知识
  • 2025年渗透测试面试题总结-204(题目+回答)
  • 复制 201/220 Dump 需要用什么?
  • idc网站备案中国与菲律宾最新事件
  • 深圳网站建设公司首选宜昌营销型网站
  • 美丽乡村 村级网站建设网站 繁体 js
  • Git 大文件上传失败深度解析与终极解决方案_含 macOS_Windows 全流程20251014
  • Starting again myself 03
  • 网站改版申请网站备案密码使用
  • 视频模型的主流结构
  • Java SpringIoCDI --- @Bean,DI
  • 深度学习与舌诊的结合:人工智能助力中医诊断新时代
  • 分治:最大子段和
  • 从江网站建设松江企业网站建设
  • 贪心算法精选30道编程题 (附有图解和源码)
  • 五莲县财源建设网站为什么网站建设图片显示不出来
  • 第11周中间件漏洞
  • 【MySQL】从零开始了解数据库开发 --- 复合查询
  • 解决 Git 推送冲突:使用 Rebase 整合远程更改
  • synchronized锁升级过程详解
  • mit6s081 lab8 locks
  • 建站培训企业管理考研
  • MySQL中的数据类型占用空间和范围
  • Docker部署jenkins集成全自动打包部署
  • 台州自助建站公司做好的网站怎么发布
  • 重磅更新:Claude Code 现在支持插件啦