linux udp广播数据包实际用例

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设备,广播数据包将不会扩散到局域网以及服务端不会收到来自局域网的数据包。

相关推荐
大聪明-PLUS2 小时前
io_uring:Linux 上的高性能异步 I/O
linux·嵌入式·arm·smarc
qq_479875432 小时前
Linux time function in C/C++【2】
linux·c语言·c++
小武~3 小时前
嵌入式网络编程深度优化 --网络协议栈配置实战指南
linux·网络·网络协议
二进制星轨3 小时前
在 Ubuntu 上快速配置 Node.js 环境(附问题说明)
linux·ubuntu·node.js
利刃大大3 小时前
【高并发服务器:HTTP应用】十四、Util工具类的设计与实现
服务器·http·高并发·项目·cpp
kblj55553 小时前
学习Linux——网络——网卡
linux·网络·学习
zhanglianzhao3 小时前
基于云服务器自建Rustdesk远程桌面——以京东云为例
运维·服务器·京东云
Physicist in Geophy.3 小时前
新版ubuntu中sac安装问题(缺少libncurses5)
linux·运维·ubuntu
可乐大数据3 小时前
Docker安装(基于云服务器ECS实例 CentOS 7.9系统)
服务器·docker·centos