廣播
前面介紹的數據包發送方式只有一個接收方,稱為單播
如果發送給局域網中的所有主機,稱為廣播
只有用戶數據報(使用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.\n", 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] = '\0'; 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\n", 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.\n", 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\n", 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.\n", 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] = '\0'; 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\n", 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.\n", 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\n", buf); } close(sockfd); return 0; }
