Linux學習之網路程式設計(組播程式設計)
言之者無罪,聞之者足以戒。 - “詩序”
1、組播IP地址
224.0.0.0 ~ 239.255.255.255
組播乙太網地址(MAC地址)
開頭高三個位元組總是 01:00:5e
2、組播分為兩部分:
1)、組播資料流 ,目的MAC地址為01:00:5e:xx:xx:xx
224.25.25.25 的mac地址為01:00:5e:19:19:19
2)、組播控制流(IGMP v1,v2,v3)
分三種報文:report(join),leave,query
3、組播和廣播的區別
廣播:使用者程序傳送一組資料,經過資料鏈路之後通過網路傳送給當前線上的所有主機。
組播:使用者程序傳送一組資料,經過資料鏈路之後通過網路傳送在組播IP範圍內的所有主機。
4、組播程式設計
server/client
client:1、IP_MULTICAST_LOOP(設定組播迴圈)
2、IP_ADD_MEMBERSHIP(新增進組播)
3、IP_DROP_MEMBERSHIP(從組播中移除)
下面看一下伺服器server的程式:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <unistd.h> #include <fcntl.h> #include <sys/epoll.h> #include <errno.h> #define MAX_LISTEN_QUE 5 #define SERV_PORT 8888 #define MAX_BUFFER_SIZE 100 #define FD_MAXSIZE 1024 #define MAX_EVENTS 10 #define RT_ERR (-1) #define RT_OK 0 #define MCAST_PORT 8888 #define MCAST_ADDR "224.25.25.25" #define MCAST_DATA "Hello.Multicast!" #define MCAST_INTERVAL 3 #define BUFF_SIZE 256 int main(int argc, char*argv) { int sockfd; struct sockaddr_in mcast_addr; char buf[100] = MCAST_DATA; int on = 1; int ret; //建立套接字 sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd == -1) { perror("socket()"); return -1; } //設定地址重用 if((ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) < 0){ perror("Error, set socket reuse addr failed"); return -1; } //結構體mcast_addr的初始化 memset(&mcast_addr, 0, sizeof(mcast_addr)); mcast_addr.sin_family = AF_INET; //IPv4 mcast_addr.sin_addr.s_addr = inet_addr(MCAST_ADDR);//繫結地址 mcast_addr.sin_port = htons(MCAST_PORT); //埠號 while(1) { //將buf中的資料傳送到mcast_addr組播中 int n = sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr*)&mcast_addr,sizeof(mcast_addr)); if( n < 0){ perror("sendto:"); return -1; } //睡眠 sleep(MCAST_INTERVAL); } return 0; }
接下來看一下客戶端client的程式:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <unistd.h> #include <fcntl.h> #include <sys/epoll.h> #define MAX_LISTEN_QUE 5 #define SERV_PORT 8888 #define MAX_BUFFER_SIZE 100 #define FD_MAXSIZE 1024 #define MAX_EVENTS 10 #define RT_ERR (-1) #define RT_OK 0 #define MCAST_PORT 8888 #define MCAST_ADDR "224.25.25.25" #define MCAST_DATA "Hello.Multicast!" #define MCAST_INTERVAL 3 #define BUFF_SIZE 256 int main(int argc, char*argv[]) { int sockfd; struct sockaddr_in local_addr; int err = -1; int ret; int on = 1; int times = 0; int addr_len = 0; char buff[BUFF_SIZE]; int n = 0; int loop = 1; //建立套接字 sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd == -1) { perror("socket()"); return -1; } //設定地址重用 if((ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) < 0){ perror("Error, set socket reuse addr failed"); return -1; } //結構體local_addr的初始化 memset(&local_addr, 0, sizeof(local_addr)); local_addr.sin_family = AF_INET;//IPv4 local_addr.sin_addr.s_addr = htonl(INADDR_ANY);//任何一個客戶端都可以連線 local_addr.sin_port = htons(MCAST_PORT);//設定埠號 //繫結local_addr到通訊套接字中 err = bind(sockfd,(struct sockaddr*)&local_addr, sizeof(local_addr)) ; if(err < 0) { perror("bind()"); return -1; } //設定IP地址為迴圈 err = setsockopt(sockfd,IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop)); if(err < 0){ perror("setsockopt():IP_MULTICAST_LOOP"); return -1; } //建立IP結構體 struct ip_mreq mreq; mreq.imr_multiaddr.s_addr = inet_addr(MCAST_ADDR); //設定要加入的組播的地址 mreq.imr_interface.s_addr = htonl(INADDR_ANY); //本地介面的IP地址(設定為所有可連線) //新增一個sockfd加入組播到中 err = setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); if (err < 0){ perror("setsockopt():IP_ADD_MEMBERSHIP"); return -1; } for(times = 0;times<5;times++){ //獲取結構體長度 addr_len = sizeof(local_addr); //將陣列清空 memset(buff, 0, BUFF_SIZE); //讀取套接字中的資料,儲存到buff中 n = recvfrom(sockfd, buff, BUFF_SIZE, 0,(struct sockaddr*)&local_addr,&addr_len); if(n == -1){ perror("recvfrom()"); break; } printf("Recv %dst message from server:%s\n", times, buff); sleep(MCAST_INTERVAL); } //五次之後將sockfd從組播中移除 err = setsockopt(sockfd, IPPROTO_IP, IP_DROP_MEMBERSHIP,&mreq, sizeof(mreq)); close(sockfd); return 0; }
應用程式通過命令字IP_ADD_MEMBERSHIP把一個socket加入到一個多播組,IP_ADD_MEMBERSHIP是一個IP層的命令字,其呼叫使用的引數是結構體struct ip_mreq,其定義如下:
struct ip_mreq
{
struct in_addr imr_multiaddr;
struct in_addr imr_interface;
};
該結構體的兩個成員分別用於指定所加入的多播組的組IP地址,和所要加入組的那個本地介面的IP地址。該命令字沒有源過濾的功能,它相當於實現IGMPv1的多播加入服務介面。
ip_setsockopt實現了該命令字,它通過呼叫ip_mc_join_group把socket加入到多播組。
IP_DROP_MEMBERSHIP把一個socket從一個多播組中移除。