Linux下UDP接收广播的方法 (linux udp 接收广播)
在网络通信中,广播是一种重要的传输方式。广播可以将一条信息同时发送给所有主机,可用于向多个主机传输同一类型的信息,例如网络状态信息、文件共享请求等。而UDP是无连接的数据报协议,其传输效率高,并且由于不需要建立连接,所以在广播中也被广泛使用。本文将介绍在Linux操作系统下使用UDP接收广播的方法。
一、UDP广播的概念
UDP广播是指发送端将UDP数据报发送到某一IP地址的所有主机,它是半双工模式的通信方式,即发送方发送信息后,不等待接收方的应答即可继续发送下一个信息。UDP广播是一种不可靠的传输方式,由于UDP本身没有错误检查、确认重传、流量控制等机制,所以广播消息在传输过程中有丢失、重复、乱序等问题。
二、UDP广播的应用场景
UDP广播可用于以下场景:
1、网络状态信息:网络管理员可以通过向网段内所有主机发送广播包,获取主机的IP地址、MAC地址等网络状态信息;
2、文件共享请求:一台主机可以通过向局域网内的所有主机发送广播试图查找其他主机共享的文件;
3、在线游戏:在线游戏中需要向所有玩家发送游戏信息,例如位置变化、消息通知等;
4、多播应用:多播应用可以利用广播技术向一组主机发送相同的信息。
三、Linux下UDP广播的接收方法
在Linux操作系统下,我们可以使用socket API来创建UDP套接字,并通过设置socket选项来实现UDP广播的接收。
1、创建UDP套接字
使用socket函数创建UDP套接字:
int socket(int domn, int type, int protocol);
该函数返回一个整型的套接字描述符,domn指定套接字的通信域,使用AF_INET表示IPv4协议;type指定套接字的类型,使用SOCK_DGRAM表示UDP数据报套接字;protocol指定协议类型,通常为0表示使用默认协议。
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
2、设置socket选项
必须设置SO_REUSEADDR选项,否则后续的bind操作可能会报错,因为套接字地址已被占用。同时,还需要设置SO_BROADCAST选项,以允许广播传输。
struct timeval timeout={5,0};//5s
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
int optval = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval));
SO_RCVTIMEO选项可以设置接收超时时间,单位为秒和微秒,当设定时间未收到数据,接收函数会返回-1,并渲染errno为EWOULDBLOCK。
3、绑定UDP端口
使用bind函数将UDP套接字绑定到一个端口上:
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
该函数将套接字描述符sockfd绑定到地址addr,并指定地址长度addrlen。在UDP广播中,可以将端口设置为0,表示让内核自动分配端口号。
struct sockaddr_in serveraddr;
bzero(&serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
serveraddr.sin_port = htons(UDP_PORT);
bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
4、接收广播数据
使用recvfrom函数从UDP套接字中接收数据:
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
该函数将从UDP套接字sockfd中接收更大长度为len的数据,存储到buf中,并将数据源地址保存在src_addr中。flags为接收标志,此处可设为0表示阻塞接收。addrlen是地址长度的指针,存储接收到的地址的长度。该函数返回实际接收的大小,如果发生错误,返回-1。
char recvbuf[UDP_MAX_LEN];
struct sockaddr_in clientaddr;
socklen_t clientaddrlen = sizeof(clientaddr);
ssize_t n = recvfrom(sockfd, recvbuf, sizeof(recvbuf), 0, (struct sockaddr *)&clientaddr, &clientaddrlen);
五、示例程序
#include
#include
#include
#include
#include
#include
#include
#define UDP_PORT (12345) // 广播端口
#define UDP_MAX_LEN (1024) // UDP数据报更大长度
int mn(int argc, char **argv)
{
// 创建UDP套接字
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sockfd
{
printf(“create socket fled!\n”);
return -1;
}
// 设置socket选项
struct timeval timeout={5,0};//5s
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
int optval = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval));
// 绑定UDP端口
struct sockaddr_in serveraddr;
bzero(&serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
serveraddr.sin_port = htons(UDP_PORT);
bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
// 接收广播数据
char recvbuf[UDP_MAX_LEN];
struct sockaddr_in clientaddr;
socklen_t clientaddrlen = sizeof(clientaddr);
while(1)
{
ssize_t n = recvfrom(sockfd, recvbuf, sizeof(recvbuf), 0, (struct sockaddr *)&clientaddr, &clientaddrlen);
if(n > 0)
{
recvbuf[n] = ‘\0’;
printf(“recv from %s:%d, data: %s\n”, inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port), recvbuf);
// TODO: 对接收到的广播数据进行处理
}
}
// 关闭UDP套接字
close(sockfd);
return 0;
}
六、