广播
前面介绍的数据包发送方式只有一个接收方,称为单播
如果发送给局域网中的所有主机,称为广播
只有用户数据报(使用UDP协议)套接字才能广播
广播地址:
以192.168.1.0(255.255.255.0)网段为例,最大的主机地址192.168.1.255代表该网段的广播地址
发送该地址的数据包被所有主机接收
255.255.255.255在所有网段中都代表广播地址
广播发送
创建用户数据报套接字
缺省创建的套接字不允许广播数据包,需要设置属性--setsockopt可以设置套接字属性
接收方指定为广播地址
指定端口信息
发送数据包
广播接收:
创建用户数据报套接字
绑定ip地址(广播ip或0.0.0.0)和端口
绑定的端口必须和发送方的指定的端口相同
等待接收数据
实例如下:
send.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #define err_log(errlog) do{perror(errlog); exit(1);}while(0) #define N 128 // ./send 192.168.0.255 10000 int main(int argc, const char *argv[]) { int sockfd; struct sockaddr_in broadcastaddr; struct sockaddr_in clientaddr; socklen_t addrlen = sizeof(clientaddr); char buf[N] = {}; if(argc < 3) { fprintf(stderr, "usage:%s serverip port. ", argv[0]); return -1; } if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { err_log("fail to socket"); } broadcastaddr.sin_family = AF_INET; broadcastaddr.sin_addr.s_addr = inet_addr(argv[1]); broadcastaddr.sin_port = htons(atoi(argv[2])); int on = 1; if(setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) { err_log("fail to setsockopt"); } while(1) { printf("Input > "); fgets(buf, N, stdin); buf[strlen(buf)-1] = ''; if(sendto(sockfd, buf, N, 0, (struct sockaddr *)&broadcastaddr, addrlen) < 0) { err_log("fail to sendto"); } if(strncmp(buf, "quit", 4) == 0) { break; } printf("%s ", buf); } close(sockfd); return 0; }
recv.c
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #define err_log(errlog) do{perror(errlog); exit(1);}while(0) #define N 128 int main(int argc, const char *argv[]) { int sockfd; struct sockaddr_in broadcastaddr; struct sockaddr_in clientaddr; socklen_t addrlen = sizeof(clientaddr); char buf[N] = {}; if(argc < 3) { fprintf(stderr, "usage:%s serverip port. ", argv[0]); return -1; } if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { err_log("fail to socket"); } broadcastaddr.sin_family = AF_INET; broadcastaddr.sin_addr.s_addr = inet_addr(argv[1]); broadcastaddr.sin_port = htons(atoi(argv[2])); if(bind(sockfd, (struct sockaddr*)&broadcastaddr, sizeof(broadcastaddr)) < 0) { err_log("fail to bind"); } while(1) { if(recvfrom(sockfd, buf, N, 0, (struct sockaddr*)&clientaddr, &addrlen) < 0) { err_log("fail to recvfrom"); } printf("From clientaddr:%s ", buf); strcat(buf, " from server..."); } close(sockfd); return 0; }
编译运行测试
./send 192.168.1.255 10000
分别在两台局域网内的机器上测试
./recv 192.168.1.255 10000 收到
./recv 192.168.1.255 10000 收到
(2)组播(多播)
单播方式只能发送给一个接收方
广播方式发给所有的主机。过多的广播会大量占用网络带宽,造成广播风暴,影响正常通信
组播(多播)是一种折中的方式只有加入某个多播组的主机才能收到数据
多播方式既可以发送给多个主机,又能避免广播那样带来带来过多的负载(每台主机要到传输层才能判断广播包是否要处理)
组播发送:
创建用户数据报套接字
接收方地址指定为组播地址
指定端口信息
发送数据包
组播接收:
创建用户数据报套接字
加入多播组
绑定ip地址(加入组的组ip或0.0.0.0)和端口---绑定的端口必须和发送方指定的端口相同
等待接收数据
加入多播组
struct ip_mreq
{
struct in_addr imr_multiaddr;
struct in_addr imr_interface;
}
struct ip_mreq req;
bzero(&req,sizeof(req))
req.imr_multiaddr.s_addr = inet_addr(argv[1]);
req.imr_interface.s_addr = htonl(INADDR_ANY);
if(setsockopt(sockfd, IPPROTO_IP,IP_ADD_MEMBERSHIP, &req, sizeof(req)) < 0)
{
err_log("fail to setsockopt");
}
send.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #define err_log(errlog) do{perror(errlog); exit(1);}while(0) #define N 128 // ./send 192.168.0.255 10000 int main(int argc, const char *argv[]) { int sockfd; struct sockaddr_in groupcastaddr; struct sockaddr_in clientaddr; socklen_t addrlen = sizeof(clientaddr); char buf[N] = {}; if(argc < 3) { fprintf(stderr, "usage:%s serverip port. ", argv[0]); return -1; } if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { err_log("fail to socket"); } groupcastaddr.sin_family = AF_INET; groupcastaddr.sin_addr.s_addr = inet_addr(argv[1]); groupcastaddr.sin_port = htons(atoi(argv[2])); while(1) { printf("Input > "); fgets(buf, N, stdin); buf[strlen(buf)-1] = ''; if(sendto(sockfd, buf, N, 0, (struct sockaddr *)&groupcastaddr, addrlen) < 0) { err_log("fail to sendto"); } if(strncmp(buf, "quit", 4) == 0) { break; } printf("%s ", buf); } close(sockfd); return 0; }
recv.c
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #define err_log(errlog) do{perror(errlog); exit(1);}while(0) #define N 128 int main(int argc, const char *argv[]) { int sockfd; struct sockaddr_in groupcastaddr; struct sockaddr_in clientaddr; socklen_t addrlen = sizeof(clientaddr); char buf[N] = {}; if(argc < 3) { fprintf(stderr, "usage:%s serverip port. ", argv[0]); return -1; } if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { err_log("fail to socket"); } groupcastaddr.sin_family = AF_INET; groupcastaddr.sin_addr.s_addr = inet_addr(argv[1]); groupcastaddr.sin_port = htons(atoi(argv[2])); if(bind(sockfd, (struct sockaddr*)&groupcastaddr, sizeof(groupcastaddr)) < 0) { err_log("fail to bind"); } struct ip_mreq req; req.imr_multiaddr.s_addr = inet_addr(argv[1]); // req.imr_interface.s_addr = inet_addr("192.168.0.196"); req.imr_interface.s_addr = htonl(INADDR_ANY); if(setsockopt(sockfd, IPPROTO_IP,IP_ADD_MEMBERSHIP, &req, sizeof(req)) < 0) { err_log("fail to setsockopt"); } while(1) { if(recvfrom(sockfd, buf, N, 0, (struct sockaddr*)&clientaddr, &addrlen) < 0) { err_log("fail to recvfrom"); } printf("From clientaddr:%s ", buf); } close(sockfd); return 0; }