嵌入式linux网络编程,广播,组播

2019-07-12 22:18发布

文章目录

1,广播

  1. 前面介绍的数据包发送方式只有一个接受方,称为单播
  2. 如果同时发给局域网中的所有主机,称为广播
  3. 只有用户数据报(使用UDP协议)套接字才能广播
  4. 广播地址
    ·以192.168.1.0 (255.255.255.0) 网段为例,最大的主机地址192.168.1.255代表该网段的广播地址
    ·发到该地址的数据包被所有的主机接收
    ·255.255.255.255在所有网段中都代表广播地址

1.1,广播收发

广播发送 广播接受 创建用户数据报套接字fd = socket(AF_INET,SOCK_DGRAM,0)
缺省创建的套接字不允许广播数据包,需要设置属性
---setsockopt可以设置套接字属性
接收方地址指定为广播地址
指定端口信息
发送数据包
创建用户数据报套接字
绑定本机IP地址和端口
---绑定的端口必须和发送方指定的端口相同
等待接收数据

1.2,广播收发—示例

1.2.1,net.h

#ifndef __NET_H__ #define __NET_H__ #include #include #include #include #include #include #include #include #include #define SERV_PORT 5005 #define QUIT_STR "quite" #endif

1.2.2,sender.c

/* boardcast demo */ /*./sender receiver_ip receiver_port */ #include "net.h" void usage(char *s) { printf("this is a boardcast demo! "); printf("Usage: %s ",s); printf(" receiver_ip: boardcast_receiver ip address "); printf(" receiver_port: boardcast_receiver port(>5000) "); } int main(int argc, const char *argv[]) { int fd; short port; struct sockaddr_in sin; if(argc != 3) { usage((char *)argv[0]); exit(1); } if((port = atoi(argv[2])) < 5000) { usage((char *)argv[0]); exit(1); } /* 1 创建socket fd(创建用户数据报套接字) */ if((fd = socket(AF_INET,SOCK_DGRAM,0)) < 0)//UDP编程 { perror("socket"); exit(-1); } /*设置允许广播*/ int b_br =1; setsockopt(fd,SOL_SOCKET,SO_BROADCAST,&b_br,sizeof(int)); /* 2 连接服务器 */ /* 2.1 填充struct sockaddr_in结构体变量*/ bzero(&sin,sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(port);//转为网络字节序端口号 #if 0 #else if(inet_pton(AF_INET,argv[1],(void *)&sin.sin_addr.s_addr) < 0) { perror("inet_pton"); goto _error1; } #endif printf("boardcast sender starting ...... OK! "); /* 3 读写*/ char buf[BUFSIZ]; while(1) { fprintf(stderr,"Please input the string to boardcast receiver:"); bzero(buf,BUFSIZ); if(fgets(buf,BUFSIZ-1,stdin) == NULL) { perror("fgets"); continue; } sendto(fd,buf,strlen(buf),0,(struct sockaddr *)&sin,sizeof(sin)); if(strncasecmp(buf,QUIT_STR,strlen(QUIT_STR)) == 0) { printf("boardcast sender is existed! "); break; } } _error1: close(fd); return 0; }

1.2.3,receiver.c

#include "net.h" int main(int argc, const char *argv[]) { int fd; struct sockaddr_in sin; /* 1 创建socket fd */ if((fd = socket(AF_INET,SOCK_DGRAM,0)) < 0)//UDP编程,套接字类型为SOCK_DGRAM { perror("socket"); exit(-1); } /* 2 允许绑定地址快速重用 */ int b_reuse = 1; setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&b_reuse,sizeof(int)); /* 3 绑定 */ /* 3.1 填充struct sockaddr_in 结构体变量*/ bzero(&sin,sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(SERV_PORT); #if 1 /* 让服务器可以绑定在任意的IP上*/ sin.sin_addr.s_addr = htonl(INADDR_ANY); #else if(inet_pton(AF_INET,SERV_IP_ADDR,(void *)&sin.sin_addr.s_addr) < 0) { perror("inet_pton"); goto _error1; } #endif /* 3.2 绑定*/ if(bind(fd,(struct sockaddr *)&sin,sizeof(sin))) { perror("bind"); goto _error1; } printf("boardcast receiver starting .... OK! "); char buf[BUFSIZ]; struct sockaddr_in cin; socklen_t addrlen = sizeof(cin); while(1) { /* 让系统自动获取客户端的信息*/ bzero(buf,BUFSIZ); if(recvfrom(fd,buf,BUFSIZ-1,0,(struct sockaddr *)&cin,&addrlen) < 0) { perror("recvfrom"); continue ; } char cin_ipv4_addr[16]; if(inet_ntop(AF_INET,&cin.sin_addr.s_addr,cin_ipv4_addr,sizeof(cin_ipv4_addr)) < 0) { perror("inet_ntop"); goto _error1; } printf("Receive broadcast Data: %s",buf); if(strncasecmp(buf,QUIT_STR,strlen(QUIT_STR)) == 0) { printf("boardcast sender (%s:%d) is existing! ",cin_ipv4_addr,ntohs(cin.sin_port)); } } _error1: close(fd); return 0; }

1.2.4,运行结果

接收端 linux@linux:~/test/network/network_broadcast$ ./receiver boardcast receiver starting .... OK! Receive broadcast Data: 1 Receive broadcast Data: 2 Receive broadcast Data: 3 Receive broadcast Data: 4 Receive broadcast Data: quite boardcast sender (192.168.1.100:34256) is existing! 发送端 linux@linux:~/test/network/network_broadcast$ ./sender 192.168.1.255 5005 boardcast sender starting ...... OK! Please input the string to boardcast receiver:1 Please input the string to boardcast receiver:2 Please input the string to boardcast receiver:3 Please input the string to boardcast receiver:4 Please input the string to boardcast receiver:quite boardcast sender is existed!

2,组播(多播)

  1. 单播方式只能发给一个接收方。
  2. 广播方式发给所有的主机。过多的广播会大量占用网络带宽,造成广播风暴,影响正常的通信。
  3. 组播(又称为多播)是一种折中的方式。只有加入某个多播组的主机才能收到数据。
  4. 多播方式既可以发给多个主机,又能避免象广播那样带来过多的负载(每台主机要到传输层才能判断广播包是否要处理)

2.1,网络地址

  1. A类地址
    第1字节为网络地址,其他3个字节为主机地址。第1字节的最高位固定为0
    1.0.0.1 – 126.255.255.255
  2. B类地址
    第1字节和第2字节是网络地址,其他2个字节是主机地址。第1字节的前两位固定为10
    128.0.0.1 – 191.255.255.255
  3. C类地址
    前3个字节是网络地址,最后1个字节是主机地址。第1字节的前3位固定为110
    192.0.0.1 – 223.255.255.255
  4. D类地址(组播地址)
    不分网络地址和主机地址,第1字节的前4位固定为1110
    224.0.0.1 – 239.255.255.255
    组播地址:224.0.0.1 – 239.255.255.255(中间除掉广播地址)

2.2,组播收发

组播发送 组播接受 创建用户数据报套接字fd = socket(AF_INET,SOCK_DGRAM,0)
接收方地址指定为组播地址
指定端口信息
发送数据包
创建用户数据报套接字
加入多播组
绑定本机IP地址和端口
---绑定的端口必须和发送方指定的端口相同
等待接收数据

2.2.1,加入多播组

struct ip_mreq { struct in_addr imr_multiaddr; struct in_addr imr_interface; }; struct ip_mreq mreq; bzero(&mreq, sizeof(mreq)); mreq.imr_multiaddr.s_addr = inet_addr(235.10.10.3); mreq.imr_interface.s_addr = htonl(INADDR_ANY); setsockopt(sockfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq));

2.3,组播收发 — 示例

2.3.1,net.h

#ifndef __NET_H__ #define __NET_H__ #include #include #include #include #include #include #include #include #include #define MULTICAST_IP "235.10.10.3" #define SERV_PORT 5003 //#define BACKLOG 5 #define QUIT_STR "quite" #endif

2.3.2,sender.c

/* multicast demo */ /*./sender receiver_ip receiver_port */ #include "net.h" void usage(char *s) { printf("this is a multicast demo! "); printf("Usage: %s ",s); printf(" receiver_ip: multicast_receiver ip address(between 224~239 segment) "); printf(" receiver_port: multicast_receiver port(>5000) "); } int main(int argc, const char *argv[]) { int fd; short port; struct sockaddr_in sin; if(argc != 3) { usage((char *)argv[0]); exit(1); } if((port = atoi(argv[2])) < 5000) { usage((char *)argv[0]); exit(1); } /* 1 创建socket fd(创建用户数据报套接字) */ if((fd = socket(AF_INET,SOCK_DGRAM,0)) < 0)//UDP编程 { perror("socket"); exit(-1); } /* 2 连接服务器 */ /* 2.1 填充struct sockaddr_in结构体变量*/ bzero(&sin,sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(port);//转为网络字节序端口号 #if 0 #else if(inet_pton(AF_INET,argv[1],(void *)&sin.sin_addr.s_addr) < 0) { perror("inet_pton"); goto _error1; } #endif printf("multicast sender starting ...... OK! "); /* 3 读写*/ char buf[BUFSIZ]; while(1) { fprintf(stderr,"Please input the string to boardcast receiver:"); bzero(buf,BUFSIZ); if(fgets(buf,BUFSIZ-1,stdin) == NULL) { perror("fgets"); continue; } sendto(fd,buf,strlen(buf),0,(struct sockaddr *)&sin,sizeof(sin)); if(strncasecmp(buf,QUIT_STR,strlen(QUIT_STR)) == 0) { printf("multicast sender is existed! "); break; } } _error1: close(fd); return 0; }

2.3.3,recviver.c

#include "net.h" int main(int argc, const char *argv[]) { int fd; struct sockaddr_in sin; /* 1 创建socket fd */ if((fd = socket(AF_INET,SOCK_DGRAM,0)) < 0)//UDP编程,套接字类型为SOCK_DGRAM { perror("socket"); exit(-1); } /* 2 允许绑定地址快速重用 */ int b_reuse = 1; setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&b_reuse,sizeof(int)); /* 加入多播组 */ struct ip_mreq mreq; bzero(&mreq, sizeof(mreq)); mreq.imr_multiaddr.s_addr = inet_addr(MULTICAST_IP); mreq.imr_interface.s_addr = htonl(INADDR_ANY); setsockopt(fd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq)); /* 3 绑定 */ /* 3.1 填充struct sockaddr_in 结构体变量*/ bzero(&sin,sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(SERV_PORT); #if 1 /* 让服务器可以绑定在任意的IP上*/ sin.sin_addr.s_addr = htonl(INADDR_ANY); #else if(inet_pton(AF_INET,SERV_IP_ADDR,(void *)&sin.sin_addr.s_addr) < 0) { perror("inet_pton"); goto _error1; } #endif /* 3.2 绑定*/ if(bind(fd,(struct sockaddr *)&sin,sizeof(sin))) { perror("bind"); goto _error1; } printf("multicast receiver starting .... OK! "); char buf[BUFSIZ]; struct sockaddr_in cin; socklen_t addrlen = sizeof(cin); while(1) { /* 让系统自动获取客户端的信息*/ bzero(buf,BUFSIZ); if(recvfrom(fd,buf,BUFSIZ-1,0,(struct sockaddr *)&cin,&addrlen) < 0) { perror("recvfrom"); continue ; } char cin_ipv4_addr[16]; if(inet_ntop(AF_INET,&cin.sin_addr.s_addr,cin_ipv4_addr,sizeof(cin_ipv4_addr)) < 0) { perror("inet_ntop"); goto _error1; } printf("Receive multicast Data: %s",buf); if(strncasecmp(buf,QUIT_STR,strlen(QUIT_STR)) == 0) { printf("multicast sender (%s:%d) is existing! ",cin_ipv4_addr,ntohs(cin.sin_port)); } } _error1: close(fd); return 0; }

2.3.4,运行结果

接收端 linux@linux:~/test/network/network_multicast$ ./receiver multicast receiver starting .... OK! Receive multicast Data: 1 Receive multicast Data: 2 Receive multicast Data: 3 Receive multicast Data: 4 Receive multicast Data: 5 Receive multicast Data: 6 Receive multicast Data: quite multic