文章目录
一、UDP是什么?
UDP(User Datagram Protocol,用户数据报协议)是一种无连接的、不可靠的、面向数据报的传输层协议,它工作在OSI(Open Systems Interconnection,开放系统互连)参考模型的第四层——传输层,与TCP(Transmission Control Protocol,传输控制协议)同属于传输层协议。然而,UDP和TCP在提供服务和处理数据的方式上存在显著差异。
UDP的主要特点
-
无连接:UDP在发送数据之前不需要建立连接,减少了开销和延迟。发送方只需将数据打包成数据报(Datagram)并发送出去,接收方负责接收并处理这些数据报。
-
不可靠传输:UDP不保证数据的可靠性,不确保数据包的顺序、完整性或是否到达。如果数据包在传输过程中丢失或出错,UDP不会进行重传或错误纠正。
-
面向数据报:UDP将网络传输的数据单位称为数据报,每个数据报的大小限制在64K字节以内。UDP对每个数据报都进行独立的处理,不将多个数据报合并或拆分。
-
低开销:由于UDP无需建立连接和进行复杂的错误检查和纠正,因此其开销比TCP小,适用于对实时性要求高且可以容忍数据丢失的应用场景,如视频流、音频流、DNS(域名系统)查询等。
-
支持广播和多播:UDP支持广播和多播,可以向网络中的多个目标同时发送数据,这在某些应用场景下非常有用,如网络会议、在线游戏等。
二、基于UDP的广播
一、基本概念
UDP广播是指由一台主机向该主机所在子网内的所有主机发送数据的方式。这种方式允许数据被子网内的所有主机接收,而不需要单独向每个主机发送数据。
二、发送范围
- 子网内广播:
- 当一台主机在子网内发送UDP广播时,该子网内的所有主机都会接收到广播数据。这是由子网掩码决定的,子网掩码用于将IP地址划分为网络部分和主机部分。广播地址是子网内主机部分全为1的IP地址。
- 例如,对于子网192.168.1.0/24(子网掩码为255.255.255.0),其广播地址是192.168.1.255。当一台主机向该广播地址发送数据时,子网192.168.1.0/24内的所有主机都将收到这些数据。
- 局域网(LAN)内广播:
- UDP广播通常局限于局域网(LAN)内使用,因为广播信息通常不会被路由器转发到其他子网或网络。这是为了防止广播数据在大型网络中造成不必要的流量和拥塞。
- 然而,如果网络中存在特殊的配置(如DHCP中继、代理ARP等),广播数据可能会被转发到其他子网或网络,但这通常不是UDP广播的默认行为。
- 受限广播:
- 受限广播地址(255.255.255.255)是一个特殊的广播地址,用于向同一物理网络上的所有主机发送广播数据。然而,这种广播通常不会被路由器转发,因此其发送范围也仅限于局域网内。
三、广播的实现
1.发送端
代码如下(示例):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define PORT 8080
#define BROADCAST_IP "192.168.159.255" // 或者使用具体的子网广播地址,如 "192.168.159.255"
int main() {
int sockfd;
struct sockaddr_in broadcastAddr;
char broadcastMessage[] = "Hello, UDP broadcast!";
// 创建UDP socket
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
perror("socket creation failed");
exit(EXIT_FAILURE);
}
// 设置广播地址
memset(&broadcastAddr, 0, sizeof(broadcastAddr));
broadcastAddr.sin_family = AF_INET;
broadcastAddr.sin_addr.s_addr = inet_addr(BROADCAST_IP);
broadcastAddr.sin_port = htons(PORT);
// 设置广播权限(操作系统默认限制了广播消息的发送)
int bcast = 1;
setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,&bcast,sizeof(bcast));
// 发送广播消息
if (sendto(sockfd, broadcastMessage, strlen(broadcastMessage), 0,
(struct sockaddr *)&broadcastAddr, sizeof(broadcastAddr)) < 0) {
perror("sendto failed");
exit(EXIT_FAILURE);
}
printf("Broadcast message sent\n");
close(sockfd);
return 0;
}
注意:如果想在本机上能够接收到广播信息,这里的 BROADCAST_IP要设置成本机子网广播地址
2.接收端
代码如下(示例):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define PORT 8080
#define BUFFER_SIZE 1024
int main() {
int sockfd;
struct sockaddr_in serverAddr, clientAddr;
socklen_t len;
char buffer[BUFFER_SIZE];
// 创建UDP socket
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
perror("socket creation failed");
exit(EXIT_FAILURE);
}
// 允许接收广播消息
int yes = 1;
if (setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &yes, sizeof(int)) < 0) {
perror("setsockopt(SO_BROADCAST) failed");
exit(EXIT_FAILURE);
}
// 绑定socket到端口
memset(&serverAddr, 0, sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
serverAddr.sin_port = htons(PORT);
if (bind(sockfd, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) < 0) {
perror("bind failed");
exit(EXIT_FAILURE);
}
// 接收广播消息
len = sizeof(clientAddr);
ssize_t n = recvfrom(sockfd, buffer, BUFFER_SIZE, 0,
(struct sockaddr *)&clientAddr, &len);
if (n < 0) {
perror("recvfrom failed");
exit(EXIT_FAILURE);
}
buffer[n] = '\0';
printf("Received broadcast: %s\n", buffer);
close(sockfd);
return 0;
}
3.测试
首先,通过ifconfig命令获取本机子网广播地址
按照下列步骤运行
gcc Broadcast_sender.c -o send
gcc Broadcast_receiver.c -o recv
分别在两个终端中分别运行下列命令
./recv
./send
三、基于UDP的多播
基于UDP的多播是一种网络通信模式,它允许数据从单个源点发送到多个目的地址,实现一对多或多对多的高效数据分发。以下是对基于UDP的多播的详细解析:
一、多播的概念
多播(Multicast)是计算机网络中的一种数据传输方式,与单播(Unicast)和广播(Broadcast)相对应。多播能够实现数据从单一源点到多个目的地的传输,而不像单播那样只能实现一对一的传输,也不像广播那样将数据包发送给网络中的所有主机。多播地址用于标识一组接收者,只有加入该多播组的成员才能接收到数据。
二、UDP多播的原理
在TCP/IP协议栈中,多播是基于UDP协议实现的。UDP多播使用特殊的IP地址范围来标识多播组,这些地址由IANA(互联网号码分配机构)进行全球分配和管理。
- IPv4多播地址范围:224.0.0.0 ~ 239.255.255.255。
-
在IPv4中,多播地址被称为D类地址,其范围是从224.0.0.0到239.255.255.255。这个范围是通过将IP地址的前四位设置为1110来定义的。多播地址的具体分类和使用情况如下:
-
局部多播地址:范围在224.0.0.0到224.0.0.255之间,这些地址保留用于本地子网,通常用于路由协议和其他内部网络功能。这些地址不会被IP路由器转发到子网之外。
-
预留多播地址:除了局部多播地址外,多播地址中还包括一些为特定用途预留的地址段,这些地址段的具体用途由相关协议和标准定义。
-
管理权限多播地址:范围在239.0.0.0到239.255.255.255之间,这些地址通常用于组织内部网络,类似于私有IP地址,不会被用于公网通信。
-
- IPv6多播地址范围:以FF开头的地址段,如FF00::/8。
发送方将数据包发送到指定的多播IP地址,网络设备(如路由器)根据多播路由协议(如IGMP、PIM等)将数据包转发到需要接收该数据的各个网段。接收方只要加入对应的多播组,就可以接收到发送到该组的数据包。
三、UDP多播的优点
- 高效性:多播数据在网络中只需传输一次,然后在需要的地方进行复制,从而减少了网络带宽的浪费和发送方的负载。
- 可扩展性:多播允许大量的接收者同时接收数据,而不需要发送方为每个接收者单独发送数据副本,因此具有良好的可扩展性。
- 灵活性:多播组可以根据需要进行动态创建和删除,接收者也可以根据需要加入或退出多播组。
四、UDP多播的应用场景
- 视频直播:将视频数据同时分发给大量观众,提高传输效率。
- 文件分发:在需要向大量客户端分发相同文件的场景中,使用多播技术可以提高分发效率。
- 在线游戏:玩家可以通过多播技术快速组建游戏房间并匹配对手。
- 监控系统:摄像头可以将视频数据同时分发给多个监控终端,提高监控效率。
- 分布式计算:在分布式计算环境中,任务调度中心可以将计算任务分发给多个节点,简化任务分发过程。
五、UDP多播的实现
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>
#define MULTICAST_ADDR "224.0.0.1"
#define MULTICAST_PORT 12345
int main() {
int sock;
struct sockaddr_in addr;
char message[] = "Hello, Multicast!";
// 创建UDP套接字
if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
perror("socket creation failed");
exit(EXIT_FAILURE);
}
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(MULTICAST_ADDR);
addr.sin_port = htons(MULTICAST_PORT);
// 发送数据
if (sendto(sock, message, strlen(message), 0, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
perror("sendto failed");
exit(EXIT_FAILURE);
}
printf("Message sent\n");
close(sock);
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 MULTICAST_ADDR "224.0.0.1"
#define MULTICAST_PORT 12345
int main() {
int sock;
struct sockaddr_in addr, recv_addr;
char buffer[1024];
socklen_t len;
// 创建UDP套接字
if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
perror("socket creation failed");
exit(EXIT_FAILURE);
}
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(MULTICAST_ADDR);
addr.sin_port = htons(MULTICAST_PORT);
// 绑定套接字到多播地址和端口
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
perror("bind failed");
exit(EXIT_FAILURE);
}
// 加入多播组
struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr = inet_addr(MULTICAST_ADDR);
mreq.imr_interface.s_addr = INADDR_ANY;
if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
perror("setsockopt failed");
exit(EXIT_FAILURE);
}
// 接收数据
len = sizeof(recv_addr);
while (1) {
int n = recvfrom(sock, buffer, sizeof(buffer), 0, (struct sockaddr *)&recv_addr, &len);
if (n < 0) {
perror("recvfrom failed");
exit(EXIT_FAILURE);
}
buffer[n] = '\0';
printf("Received: %s\n", buffer);
}
close(sock);
return 0;
}