多播:向加入特定组的大量主机以UDP的方式同时传送数据
特点:
1、发送方只针对特定多播组发送一次数据
2、在特定多播组内所有接受者都会收到发往改组的数据
3、组数在IP(组播属于D类IP)地址范围内任意增加
4、基于UDP完成,多播数据包格式与UDP数据包相同
5、借助路由完成数据包的复制、传输(到主机)
设置TTL与加入多播组
要向使用多播必须设置TTL,TTL决定了数据包传递距离:数据包每经过一个路由器TTL就会减一,减到零时数据包无法被继续传递,会被销毁
注:TTL过大影响网络流量,过小可能无法传递到目标
ip_mreq结构体
struct ip_mreq
{
struct in_addr imr_multiaddr; //多播组的IP地址
struct in_addr imr_interface; //加入该组的套接字的所属主机IP地址,可使用INADDR_ANY
}
发送方设置TTL
int send_sock;
int time_live = ttl大小;
…..
send_sock = socket(PF_INET, SOCK_DGRAM, 0);
setsockopt(send_sock, IPPROTO_IP, IP_MULTICAST_TTL, & time_live, sizeof(time_live));
….
接收方加入多播组
int recv_sock;
struct ip_mreq join_adr;
…
send_sock = socket(PF_INET, SOCK_DGRAM, 0);
…
join_adr. imr_multiaddr.s_addr = 多播组IP;
join_adr. imr_interface.s_addr = 加入多播组的主机IP地址;
setsockopt(recv_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, & join_adr, sizeof(join_adr));
.....
示例代码:
发送方
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define TTL 64 //决定数据包经过第64个路由时销毁
#define BUF_SIZE 30
void error_handling(const char *message);
int main(int argc, const char * argv[])
{
int send_sock = -1;
struct sockaddr_in mul_adr;
int time_live = TTL;
FILE *fp = NULL;
char buf[BUF_SIZE] = { 0 };
if( 3 != argc )
{
printf("Usage : %s <GroupIp> <Port> \n", argv[0]);
exit(1);
}
send_sock = socket(PF_INET, SOCK_DGRAM, 0);//多播基于UDP
memset(&mul_adr, 0, sizeof(mul_adr));
mul_adr.sin_family = AF_INET;
mul_adr.sin_addr.s_addr = inet_addr(argv[1]); //此处需要设置数据目标为多播组IP地址
mul_adr.sin_port = htons(atoi(argv[2])); //目标端口
setsockopt(send_sock, IPPROTO_IP, IP_MULTICAST_TTL, (void *)&time_live, sizeof(time_live));//设置TTL
if( NULL == (fp = fopen("./test.txt", "r")) )
error_handling("fopen() error");
while (!feof(fp)) //feof函数,文件结束返回非0值,否则返回0
{
fgets(buf, BUF_SIZE, fp);
sendto(send_sock, buf, strlen(buf), 0, (struct sockaddr *)&mul_adr, sizeof(mul_adr));
sleep(1); //传输数据时间间隔,没有特殊意义
}
fclose(fp);
close(send_sock);
return 0;
}
void error_handling(const char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
接收方
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define BUF_SIZE 30
void error_handling(const char *message);
int main(int argc, const char * argv[])
{
int recv_sock = -1;
int str_len = -1;
char buf[BUF_SIZE] = { 0 };
struct sockaddr_in adr; //绑定地址
struct ip_mreq join_adr; //多播组结构地址相关
if( 3 != argc )
{
printf("Usage : %s <GroupIp> <Port> \n", argv[0]);
exit(1);
}
recv_sock = socket(PF_INET, SOCK_DGRAM, 0);
memset(&adr, 0, sizeof(adr));
adr.sin_family = AF_INET;
adr.sin_addr.s_addr = htonl(INADDR_ANY);
adr.sin_port = htons(atoi(argv[2])); //注意此处的端口必须与发送方的目标端口一致,之后的广播也一样
if( -1 == bind(recv_sock, (struct sockaddr *)&adr, sizeof(adr)) )
error_handling("bind() error");
join_adr.imr_multiaddr.s_addr = inet_addr(argv[1]); //多播组IP
join_adr.imr_interface.s_addr = htonl(INADDR_ANY);
setsockopt(recv_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)&join_adr, sizeof(join_adr)); //加入多播组
while (1)
{
str_len = recvfrom(recv_sock, buf, BUF_SIZE - 1, 0, NULL, 0);//不需要发送者地址信息,将最后两个参数分别设置为NULL与0
if(str_len < 0)
break;
buf[str_len] = 0;
fputs(buf, stdout);
}
close(recv_sock);
return 0;
}
void error_handling(const char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
广播
多播可以跨网络传输数据,只要加入了多播组,广播只能在同一网络中传输数据,广播也是基于UDP
广播分类:
直接广播:广播的IP地址除网络地址外,主机地址全部设置为1,如向192.12.34中所有主机发送数据,可向192.12.34.255发送(一个是位,一个是点分十进制)
本地广播:广播的IP地址限定为255.255.255.255,如192.12.34网络中的主机向255.255.255.255发送数据,数据将传递到192.12.34网络中所有主机
套接字默认关闭广播,需要打开:
int send_sock;
int bcast = 1;
…..
send_sock = socket(PF_INET, SOCK_DGRAM, 0);
setsockopt(send_sock, SOL_SOCKET, SO_BROADCAST, & bcast, sizeof(bcast));
…
注;只需要在发送方中打开广播,无需打开接收方,其他部分与普通UDP编程无异