IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> 十四、TCP IP网络编程之多播与广播 -> 正文阅读

[网络协议]十四、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
  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2021-08-07 12:25:58  更:2021-08-07 12:27:06 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 18:46:29-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码