这一章主要是IP地址相关的通信方式,多播可以跨网络通信,而广播只能在网内通信。多播之所以能实现跨网通信。
多播
多播的传输方式采用UDP实现,与UDP的服务端、客户端之间的实现方式相近,区别在于UDP数据传输以单一对象进行数据通信,而多播数据同时传递到加入(注册)特定组的大量主机。换言之,可以同时向多个主机传递数据。
多播是采用D类的IP地址(224.0.0.0~239.255.255.255),而加入“多播组”可以理解为通过程序完成以下声明:
“在D类IP地址中,我希望接收发往目标239.234.218.234的多播数据”
在多播当中数据包的传输主要是依靠路由器之间的协调进行复制与转发。
加入多播组
在多播当中,路由器并不是无限制的复制与转发,必须有界限,这个界限就是“数据包传递距离”,我们用TTL表示,数据包每次经过路由器,数据包当中的TTL就会减一。
设置TTL以及加入多播的方法
int recv_sock;
struct ip_mreq,join_adr;
.....
recv_sock=socket(PF_INET,SOCK_DGRAM,0);
.....
join_adr.imr_mutiaddr.s_addr="多播组地址信息";
join_adr.imr_interface.s_addr="加入多播组的主机地址信息";
setsockopt(recv_sock,IPPROTO_IP,IP_ADD_MEMBERSHIP,(void*)&join_adr,sizeof(join_adr));
.....
----------------------------------------------------------------------
测试程序
实现多播,我们采用发送者(sender)和接受者(receiver)代替客户端与服务端,发送者是数据的发送主体,接受者是需要加入多播组的数据接收者。
先在工程文件当中新建一个news.txt文件,并在里面添加一些内容。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define TTL 64
#define BUF_SIZE 30
void error_handling(char *message);
int main(int argc, char const *argv[])
{
int send_sock;
struct sockaddr_in send_adr;
int time_live=TTL;
FILE* fp;
char buf[BUF_SIZE];
if(argc!=3)
{
printf("Usage :%s <GroupIp> <PORT> \n",argv[0]);
exit(1);
}
send_sock=socket(PF_INET,SOCK_DGRAM,0);
memset(&send_adr,0,sizeof(send_adr));
send_adr.sin_family=AF_INET;
send_adr.sin_addr.s_addr=inet_addr(argv[1]);
send_adr.sin_port=htons(atoi(argv[2]));
setsockopt(send_sock,IPPROTO_IP,IP_MULTICAST_TTL,(void *)&time_live,sizeof(time_live));
if((fp = fopen("news.txt","r"))==NULL)error_handling("fopen() error");
while(!feof(fp))
{
fgets(buf,BUF_SIZE,fp);
sendto(send_sock,buf,strlen(buf),0,(struct sockaddr*)&send_adr,sizeof(send_adr));
sleep(2);
}
fclose(fp);
close(send_sock);
return 0;
}
void error_handling(char *message)
{
fputs(message,stderr);
fputc('\n',stderr);
exit(1);
}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define BUF_SIZE 30
void error_handling(char *message);
int main(int argc, char const *argv[])
{
int recv_sock;
int str_len;
char buf[BUF_SIZE];
struct sockaddr_in adr;
struct ip_mreq join_adr;
if(argc!=3)
{
printf("Usage : %s <GroupId> <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(bind(recv_sock,(struct sockaddr*)&adr,sizeof(adr))==-1)error_handling("bind() error");
join_adr.imr_interface.s_addr=htonl(INADDR_ANY);
join_adr.imr_multiaddr.s_addr=inet_addr(argv[1]);
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);
if(str_len<0)break;
buf[str_len]=0;
fputs(buf,stdout);
}
close(recv_sock);
return 0;
}
void error_handling(char *message)
{
fputs(message,stderr);
fputc('\n',stderr);
exit(1);
}
运行测试:
./news_sender 224.1.1.2 9091
./news_receiver 224.1.1.2 9091
广播
广播与多播都是通过UDP实现,广播分为以下两种:直接广播与本地广播。主要区别在于IP地址,直接广播的IP地址除了网络地址,其他主机地址全部设置成1(即255),在在同一主机地址的主机都是在同一网络当中,这也说明了广播是在网内进行的。本地广播使用的IP地址限制为255.255.255.255,例如192.32.24.xxx网络当中的主机向255.255.255.255传输数据时,数据会传递到192.32.24.xxx的所有主机中。
如何设置广播
默认生成的套接字会阻止广播,因此需要修改选项来支持广播
int send_sock;
int bcast=1;
...
send_sock=socket(PF_INET,SOCK_DGRAM,0);
setsockopt(send_sock,SOL_SOCKET,SO_BROADCAST,(void*)&bcast,sizeof(bcast));
测试程序
先在工程文件当中新建一个news.txt文件,并在里面添加一些内容。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define TTL 64
#define BUF_SIZE 30
void error_handling(char *message);
int main(int argc, char const *argv[])
{
int send_sock;
struct sockaddr_in broad_adr;
FILE* fp;
char buf[BUF_SIZE];
int so_brd=1;
if(argc!=3)
{
printf("Usage :%s <GroupIp> <PORT> \n",argv[0]);
exit(1);
}
send_sock=socket(PF_INET,SOCK_DGRAM,0);
memset(&broad_adr,0,sizeof(broad_adr));
broad_adr.sin_family=AF_INET;
broad_adr.sin_addr.s_addr=inet_addr(argv[1]);
broad_adr.sin_port=htons(atoi(argv[2]));
setsockopt(send_sock,SOL_SOCKET,SO_BROADCAST,(void*)so_brd,sizeof(so_brd));
if((fp = fopen("news.txt","r"))==NULL)error_handling("fopen() error");
while(!feof(fp))
{
fgets(buf,BUF_SIZE,fp);
sendto(send_sock,buf,strlen(buf),0,(struct sockaddr*)&broad_adr,sizeof(broad_adr));
sleep(2);
}
fclose(fp);
close(send_sock);
return 0;
}
void error_handling(char *message)
{
fputs(message,stderr);
fputc('\n',stderr);
exit(1);
}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define BUF_SIZE 30
void error_handling(char *message);
int main(int argc, char const *argv[])
{
int recv_sock;
int str_len;
char buf[BUF_SIZE];
struct sockaddr_in adr;
struct ip_mreq join_adr;
if(argc!=3)
{
printf("Usage : %s <GroupId> <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(bind(recv_sock,(struct sockaddr*)&adr,sizeof(adr))==-1)error_handling("bind() error");
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);
if(str_len<0)break;
buf[str_len]=0;
fputs(buf,stdout);
}
close(recv_sock);
return 0;
}
void error_handling(char *message)
{
fputs(message,stderr);
fputc('\n',stderr);
exit(1);
}
运行结果(由于条件限制采用本地广播)
./sender 255.255.255.255 9091
./receiver 9091
|