1.说明
本节说明的是在实际案例中使用udp广播数据包产生的问题以及记录。
2.广播数据包服务端与客户端代码用例
2.1 服务端客户端广播数据包发送与接收
第一种场景,服务端部署在A机器,客户端部署在B机器。 服务端代码server.c,如下:
c
#include <unistd.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <unistd.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ifaddrs.h>
#include <net/if.h>
#define UDP_NOTIFY_PORT 20000
int udp_socket_server_init(struct sockaddr_in *saddr)
{
int optval = 1, fd = -1;
if((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{
printf("cannot setup socket \n");
return -1;
}
if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(int)) < 0)
{
printf("cannot set socket setting \n");
close(fd);
return -1;
}
// struct ifreq ifr;
// memset(&ifr, 0, sizeof(ifr));
// snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "lo");
// if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr)) < 0) {
// printf("setsockopt fail\n");
// }
saddr->sin_family= AF_INET;
saddr->sin_port=htons(UDP_NOTIFY_PORT);
saddr->sin_addr.s_addr= htonl(INADDR_BROADCAST);
if(-1 == bind(fd,(struct sockaddr *)saddr,sizeof(struct sockaddr)))
{
printf("Failed to bind socket server\n");
close(fd);
return -1;
}
return fd;
}
// unsigned char matchLocalIp(char *pSrcIp, int ipLen)
// {
// struct ifaddrs *ifaddr, *ifa;
// char ip[INET_ADDRSTRLEN];
// if(ip == NULL)
// return 0;
// unsigned char findIp = 0;
// if (getifaddrs(&ifaddr) == -1) {
// return findIp;
// }
// for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
// if (ifa->ifa_addr == NULL)
// continue;
// const int family = ifa->ifa_addr->sa_family;
// if (family != AF_INET && family != AF_INET6) {
// continue;
// }
// const char *ifa_name = ifa->ifa_name;
// printf("ifa name: %s\n", ifa_name);
// struct sockaddr_in *addr = (struct sockaddr_in *)ifa->ifa_addr;
// inet_ntop(AF_INET, &addr->sin_addr, ip, INET_ADDRSTRLEN);
// printf("interface: %s, IP: %s\n", ifa->ifa_name, ip);
// if((ipLen == strlen(ip)) && (strncmp(ip, pSrcIp, ipLen) == 0))
// {
// findIp = 1;
// }
// }
// freeifaddrs(ifaddr);
// return findIp;
// }
int main()
{
struct sockaddr_in udp_addr = {0}, addr_len = {0};
int udp_fd = udp_socket_server_init(&udp_addr);
if (udp_fd < 0) {
printf("failed to init server\n");
return -1;
}
int buffer = 0;
socklen_t len = sizeof(addr_len);
int n = 0;
while (1) {
memset(&buffer, 0, sizeof(buffer));
n = 0;
while (n <= 0 ) {
printf("Start recvfrom form broadcast.\n");
n = recvfrom(udp_fd, &buffer, sizeof(buffer), MSG_WAITALL,
(struct sockaddr *)&udp_addr, &len);
if (n <= 0) {
printf("Error while receiving broadcast\n");
continue;
}
printf("client IPAddr = %s, Port = %d\n", inet_ntoa(udp_addr.sin_addr), ntohs(udp_addr.sin_port));
switch (buffer)
{
case 0 : // host reset event
{
//printf("client IPAddr = %s\n", (char * )inet_ntoa(udp_addr.sin_addr));
n = -1; // continue with n <= 0
printf(" buffer = %d\n", buffer);
// char *ip = inet_ntoa(udp_addr.sin_addr);
// if(ip == NULL)
// {
// continue;
// }
// if(matchLocalIp(ip,strlen(ip)) == 0)
// {
// printf("from other device ,should ignore.\n");
// }
// else
// {
// printf("from self device ,should not ignore.\n");
// }
}
break;
case 1 :
{
printf("[line:%d] Received broadcast, buffer = %d\n", __LINE__,buffer);
}
break;
default :
{
printf("[line:%d] Received broadcast, buffer = %d\n", __LINE__,buffer);
}
break;
}
}
}
return 0;
}
客户端代码client.c,如下:
c
#include <unistd.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#define UDP_NOTIFY_PORT 20000
int Socket;
struct sockaddr_in SockAddr;
int udp_socket_client_init()
{
int optval = 1;
struct ifreq ifr;
if((Socket = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{
printf("cannot setup socket \n");
return -1;
}
if(fcntl(Socket,F_SETFL,O_NONBLOCK) < 0)
{
printf("cannot set IO mechanism \n");
close(Socket);
return -1;
}
if(setsockopt(Socket,SOL_SOCKET,SO_BROADCAST|SO_REUSEADDR,&optval,sizeof(int)) < 0)
{
printf("cannot set socket setting \n");
close(Socket);
return -1;
}
// memset(&ifr, 0, sizeof(ifr));
// snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "lo");
// if (setsockopt(Socket, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr)) < 0) {
// printf("setsockopt fail\n");
// }
SockAddr.sin_family= AF_INET;
SockAddr.sin_port=htons(UDP_NOTIFY_PORT);
SockAddr.sin_addr.s_addr= inet_addr("255.255.255.255");
}
void UDPNotify(int initialized)
{
int retval = 0;
int event[1] = {initialized};
socklen_t len = sizeof(struct sockaddr);
int ret = 0;
retval = sendto(Socket, event, 1, 0, (struct sockaddr *)&SockAddr, len);
if(retval < 0)
{
printf("Fail to broadcast\n");
}
}
int main()
{
udp_socket_client_init();
UDPNotify(1);
return 0;
}
分别在A机器与B机器编译执行:
c
# gcc server.c -o server //A机器
# ./server //A机器
# gcc client.c -o client //B机器
# ./client
A机器服务端打印结果如下:

或者在B机器执行如下命令:
c
# echo "1" | socat - UDP4-DATAGRAM:255.255.255.255:20000,broadcast
打印数据如下:

可以在A机器部署2个服务端,可以看到如下打印:

2.2 客户端本机广播数据包
客户端可以绑定本机,只在本机广播数据包,而不是广播到整个局域网。
客户端代码client.c如下,绑定lo网络设备:
c
#include <unistd.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#define UDP_NOTIFY_PORT 20000
int Socket;
struct sockaddr_in SockAddr;
int udp_socket_client_init()
{
int optval = 1;
struct ifreq ifr;
if((Socket = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{
printf("cannot setup socket \n");
return -1;
}
if(fcntl(Socket,F_SETFL,O_NONBLOCK) < 0)
{
printf("cannot set IO mechanism \n");
close(Socket);
return -1;
}
if(setsockopt(Socket,SOL_SOCKET,SO_BROADCAST|SO_REUSEADDR,&optval,sizeof(int)) < 0)
{
printf("cannot set socket setting \n");
close(Socket);
return -1;
}
memset(&ifr, 0, sizeof(ifr));
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "lo");
if (setsockopt(Socket, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr)) < 0) {
printf("setsockopt fail\n");
}
SockAddr.sin_family= AF_INET;
SockAddr.sin_port=htons(UDP_NOTIFY_PORT);
SockAddr.sin_addr.s_addr= inet_addr("255.255.255.255");
}
void UDPNotify(int initialized)
{
int retval = 0;
int event[1] = {initialized};
socklen_t len = sizeof(struct sockaddr);
int ret = 0;
retval = sendto(Socket, event, 1, 0, (struct sockaddr *)&SockAddr, len);
if(retval < 0)
{
printf("Fail to broadcast\n");
}
}
int main()
{
udp_socket_client_init();
UDPNotify(1);
return 0;
}
这个时候仍将客户端部署在B机器上,执行之后,可以看到A机器(服务端)不会收到客户端数据包
那么将客户端部署到A机器上,(客户端与服务端在一台机器上),这个时候可以看到服务端能收到客户端的数据包。
2.3 服务端本机广播数据包
修改服务端server.c代码如下,绑定lo网络设备:
c
#include <unistd.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <unistd.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ifaddrs.h>
#include <net/if.h>
#define UDP_NOTIFY_PORT 20000
int udp_socket_server_init(struct sockaddr_in *saddr)
{
int optval = 1, fd = -1;
if((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{
printf("cannot setup socket \n");
return -1;
}
if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(int)) < 0)
{
printf("cannot set socket setting \n");
close(fd);
return -1;
}
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "lo");
if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr)) < 0) {
printf("setsockopt fail\n");
}
saddr->sin_family= AF_INET;
saddr->sin_port=htons(UDP_NOTIFY_PORT);
saddr->sin_addr.s_addr= htonl(INADDR_BROADCAST);
if(-1 == bind(fd,(struct sockaddr *)saddr,sizeof(struct sockaddr)))
{
printf("Failed to bind socket server\n");
close(fd);
return -1;
}
return fd;
}
// unsigned char matchLocalIp(char *pSrcIp, int ipLen)
// {
// struct ifaddrs *ifaddr, *ifa;
// char ip[INET_ADDRSTRLEN];
// if(ip == NULL)
// return 0;
// unsigned char findIp = 0;
// if (getifaddrs(&ifaddr) == -1) {
// return findIp;
// }
// for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
// if (ifa->ifa_addr == NULL)
// continue;
// const int family = ifa->ifa_addr->sa_family;
// if (family != AF_INET && family != AF_INET6) {
// continue;
// }
// const char *ifa_name = ifa->ifa_name;
// printf("ifa name: %s\n", ifa_name);
// struct sockaddr_in *addr = (struct sockaddr_in *)ifa->ifa_addr;
// inet_ntop(AF_INET, &addr->sin_addr, ip, INET_ADDRSTRLEN);
// printf("interface: %s, IP: %s\n", ifa->ifa_name, ip);
// if((ipLen == strlen(ip)) && (strncmp(ip, pSrcIp, ipLen) == 0))
// {
// findIp = 1;
// }
// }
// freeifaddrs(ifaddr);
// return findIp;
// }
int main()
{
struct sockaddr_in udp_addr = {0}, addr_len = {0};
int udp_fd = udp_socket_server_init(&udp_addr);
if (udp_fd < 0) {
printf("failed to init server\n");
return -1;
}
int buffer = 0;
socklen_t len = sizeof(addr_len);
int n = 0;
while (1) {
memset(&buffer, 0, sizeof(buffer));
n = 0;
while (n <= 0 ) {
printf("Start recvfrom form broadcast.\n");
n = recvfrom(udp_fd, &buffer, sizeof(buffer), MSG_WAITALL,
(struct sockaddr *)&udp_addr, &len);
if (n <= 0) {
printf("Error while receiving broadcast\n");
continue;
}
printf("client IPAddr = %s, Port = %d\n", inet_ntoa(udp_addr.sin_addr), ntohs(udp_addr.sin_port));
switch (buffer)
{
case 0 : // host reset event
{
//printf("client IPAddr = %s\n", (char * )inet_ntoa(udp_addr.sin_addr));
n = -1; // continue with n <= 0
printf(" buffer = %d\n", buffer);
// char *ip = inet_ntoa(udp_addr.sin_addr);
// if(ip == NULL)
// {
// continue;
// }
// if(matchLocalIp(ip,strlen(ip)) == 0)
// {
// printf("from other device ,should ignore.\n");
// }
// else
// {
// printf("from self device ,should not ignore.\n");
// }
}
break;
case 1 :
{
printf("[line:%d] Received broadcast, buffer = %d\n", __LINE__,buffer);
}
break;
default :
{
printf("[line:%d] Received broadcast, buffer = %d\n", __LINE__,buffer);
}
break;
}
}
}
return 0;
}
客户端仍然部署在B机器上,但是可以广播到局域网,client.c代码如下:
c
#include <unistd.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#define UDP_NOTIFY_PORT 20000
int Socket;
struct sockaddr_in SockAddr;
int udp_socket_client_init()
{
int optval = 1;
struct ifreq ifr;
if((Socket = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{
printf("cannot setup socket \n");
return -1;
}
if(fcntl(Socket,F_SETFL,O_NONBLOCK) < 0)
{
printf("cannot set IO mechanism \n");
close(Socket);
return -1;
}
if(setsockopt(Socket,SOL_SOCKET,SO_BROADCAST|SO_REUSEADDR,&optval,sizeof(int)) < 0)
{
printf("cannot set socket setting \n");
close(Socket);
return -1;
}
// memset(&ifr, 0, sizeof(ifr));
// snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "lo");
// if (setsockopt(Socket, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr)) < 0) {
// printf("setsockopt fail\n");
// }
SockAddr.sin_family= AF_INET;
SockAddr.sin_port=htons(UDP_NOTIFY_PORT);
SockAddr.sin_addr.s_addr= inet_addr("255.255.255.255");
}
void UDPNotify(int initialized)
{
int retval = 0;
int event[1] = {initialized};
socklen_t len = sizeof(struct sockaddr);
int ret = 0;
retval = sendto(Socket, event, 1, 0, (struct sockaddr *)&SockAddr, len);
if(retval < 0)
{
printf("Fail to broadcast\n");
}
}
int main()
{
udp_socket_client_init();
UDPNotify(1);
return 0;
}
可以看到A服务端是收不到B客户端的数据的。
将客户端部署在A机器上,然后绑定lo设备:
c
#include <unistd.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#define UDP_NOTIFY_PORT 20000
int Socket;
struct sockaddr_in SockAddr;
int udp_socket_client_init()
{
int optval = 1;
struct ifreq ifr;
if((Socket = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{
printf("cannot setup socket \n");
return -1;
}
if(fcntl(Socket,F_SETFL,O_NONBLOCK) < 0)
{
printf("cannot set IO mechanism \n");
close(Socket);
return -1;
}
if(setsockopt(Socket,SOL_SOCKET,SO_BROADCAST|SO_REUSEADDR,&optval,sizeof(int)) < 0)
{
printf("cannot set socket setting \n");
close(Socket);
return -1;
}
memset(&ifr, 0, sizeof(ifr));
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "lo");
if (setsockopt(Socket, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr)) < 0) {
printf("setsockopt fail\n");
}
SockAddr.sin_family= AF_INET;
SockAddr.sin_port=htons(UDP_NOTIFY_PORT);
SockAddr.sin_addr.s_addr= inet_addr("255.255.255.255");
}
void UDPNotify(int initialized)
{
int retval = 0;
int event[1] = {initialized};
socklen_t len = sizeof(struct sockaddr);
int ret = 0;
retval = sendto(Socket, event, 1, 0, (struct sockaddr *)&SockAddr, len);
if(retval < 0)
{
printf("Fail to broadcast\n");
}
}
int main()
{
udp_socket_client_init();
UDPNotify(1);
return 0;
}
A服务端能收到数据。

因此,服务端与客户端均绑定lo设备,广播数据包将不会扩散到局域网以及服务端不会收到来自局域网的数据包。