十四、TCP IP网络编程之多播与广播

这一章主要是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="加入多播组的主机地址信息"//加入多播组也通过设置套接字选项完成。加入多播组相关协议层为IPPROTO_IP,选项名为IP_ADD_MEMBERSHIP。
//ip_mreq结构体:
//   struct ip_mreq
//    {
//        struct in_addr imr_multiaddr;//写入加入的组IP
//        struct in_addr imr_interface;//加入该组的套接字所属主机的IP地址
//    }
setsockopt(recv_sock,IPPROTO_IP,IP_ADD_MEMBERSHIP,(void*)&join_adr,sizeof(join_adr));
.....
----------------------------------------------------------------------

测试程序

实现多播,我们采用发送者(sender)和接受者(receiver)代替客户端与服务端,发送者是数据的发送主体,接受者是需要加入多播组的数据接收者。

先在工程文件当中新建一个news.txt文件,并在里面添加一些内容。

//news_sender.c
#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");
    /**
     * @brief  多播
     * @note   
     * @retval 
     */
    //feof()是检测流上的文件结束符的函数,如果文件结束,则返回非0值,否则返回0
    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);
}



//news_receiver.c
#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;  //将SO_BROADCAST可选项修改为1(支持广播)
...
send_sock=socket(PF_INET,SOCK_DGRAM,0);
setsockopt(send_sock,SOL_SOCKET,SO_BROADCAST,(void*)&bcast,sizeof(bcast));

测试程序

先在工程文件当中新建一个news.txt文件,并在里面添加一些内容。

//news_sender_brd.c
#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");
    /**
     * @brief  多播
     * @note   
     * @retval 
     */
    //feof()是检测流上的文件结束符的函数,如果文件结束,则返回非0值,否则返回0
    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);
}

//news_receiver_brd.c
#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);
}

运行结果(由于条件限制采用本地广播)

./sender 255.255.255.255 9091
./receiver 9091
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值