网络编程四:UDP广播+UDP组播

一、广播

1、什么是广播

    单播:数据包发送方式只有一个接受方
    广播:同时发给局域网中的所有主机

2、特点

    只有用户数据报套接字(使用UDP协议)才能广播。

3、广播地址

        以192.168.14.0网段为例:***.***.***.255 代表该网段的广播地址。发送给该地址的数据包被所有主机接收。

   比如我们当前教室的局域网段 是 192.168.14.0 ,那么广播地址就是 192.168.14.255
   
   sendto("你好",192.168.14.255)

4、实现广播的过程(一定是使用UDP协议)

广播发送端:

1、创建数据报套接字  UDP
    int socketfd = socket(AF_INET,SOCK_DGRAM,0);
    
2、设置socketfd套接字文件描述符的属性为 广播 。(也就是允许发送广播数据包) 
    SO_BROADCAST -----》使用广播方式传送
    
    int on=1;
    setsockopt(sockfd , SOL_SOCKET,SO_BROADCAST,&on, sizeof(on));

3、发送数据 ,指定接收方为广播地址
    struct sockaddr_in sendAddr;
    sendAddr.sin_family = AF_INET;
    sendAddr.sin_port = htons(10000);
    sendAddr.sin_addr.s_addr = inet_addr("192.168.14.255");//一定是广播地址,广播发送
    sendto(sockfd,buf,strlen(buf),0,(struct sockaddr*)&sendAddr,sizeof(sendAddr));
    
4、关闭 

广播接收方:

1、创建用户数据报套接字 
    int socketfd = socket(AF_INET,SOCK_DGRAM,0);

2、绑定(192.168.14.255)广播IP地址和端口号 (10000)
        注意:绑定的端口必须和发送方指定的端口相同

    struct sockaddr_in ownAddr;
    ownAddr.sin_family = AF_INET;
    ownAddr.sin_port = htons(10000);
    //uint32_t htonl(uint32_t hostlong);  将 主机IP转为 网络IP
    ownAddr.sin_addr.s_addr = htonl(INADDR_ANY);//INADDR_ANY(0.0.0.0) 代表本机所有的地址  //inet_addr("192.168.14.255");

3、接收数据 
    recvfrom(。。。。);

5、扩展函数

#include <arpa/inet.h>

uint32_t htonl(uint32_t hostlong);

uint16_t htons(uint16_t hostshort);

uint32_t ntohl(uint32_t netlong);

uint16_t ntohs(uint16_t netshort);

h:host 代表主机 
to: 代表 转换的意思 
n: network 代表 网络 
l:unsigned long int(64位下 8个字节  32位 下 4个字节) 无符号32位  代表 IP地址 
s: unsigned short int 无符号16位 代表 端口号

6、练习

把广播通信进行实现。

广播发送端:

#include<stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>


int main()
{
	printf("广播发送端(UDP协议)...............\n");
	
	//1、创建数据报套接字
	int socketfd = socket(AF_INET,SOCK_DGRAM,0);
	if(socketfd == -1)
	{
		perror("socket error");
		return -1;
	}
	//2、设置套接字文件描述符socketfd的属性为广播(也就是允许发送广播数据包) 
	int on=1;
	setsockopt(socketfd,SOL_SOCKET,SO_BROADCAST,&on,sizeof(on));
	
	//3、发送数据,并且指定接收方为广播地址
	struct sockaddr_in sendAddr;//IPV4地址结构体变量
	sendAddr.sin_family = AF_INET;
	sendAddr.sin_port = htons(10000);
	sendAddr.sin_addr.s_addr = inet_addr("192.168.14.255");//一定是广播地址,广播发送
	
	while(1)
	{
		char buf[1024]={0};
		printf("data:");
		scanf("%s",buf); //100+"hello"
		sendto(socketfd,buf,strlen(buf),0,( struct sockaddr *)&sendAddr,sizeof(sendAddr));
	}
	
	close(socketfd);
	
	return 0;
	
}

广播接收端:

#include<stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>

int main()
{
	printf("广播接收端(UDP协议)...............\n");
	
	//1、创建数据报套接字
	int socketfd = socket(AF_INET,SOCK_DGRAM,0);
	if(socketfd == -1)
	{
		perror("socket error");
		return -1;
	}
	//2、绑定(192.168.14.255)广播IP地址和端口号 (10000)
	struct sockaddr_in ownAddr;
	ownAddr.sin_family = AF_INET;
	ownAddr.sin_port = htons(10000); //s unsigned short int 	
	ownAddr.sin_addr.s_addr = htonl(INADDR_ANY);//INADDR_ANY(0.0.0.0) 代表本机所有的地址  //inet_addr("192.168.14.255");
	
	bind(socketfd, (struct sockaddr *)&ownAddr,sizeof(ownAddr));
	
	struct sockaddr_in otherAddr;
	int len = sizeof(struct sockaddr_in);
	
	while(1)
	{
		char buf[1024]={0};
		
		recvfrom(socketfd,buf,sizeof(buf),0, (struct sockaddr *)&otherAddr,&len);

		printf("来自 %s:%u  recv:%s\n",inet_ntoa(otherAddr.sin_addr),ntohs(otherAddr.sin_port),buf);
	}
	
	close(socketfd);
	
	return 0;
	
}

二、组播(群聊)

1、概念

    组播是介于单播与广播之间,在一个局域网内,将某些主机添加到组中,并设置一个组地址
    我们只需要将数据发送到组播地址即可,加入到该组的所有主机都能接收到数据

2、组播特点

        1.需要给组播设置IP地址,该IP必须是D类地址
        2.只有UDP才能设置组播

3、IP地址分类

        IP地址 =  网络号 +  主机号

        网络号:指的是不同的网络
        主机号:指的是同一个网段下用来识别不同的主机。那也就是说,主机号所占的位数越多,在该网段下的主机数越多。
        
        A类地址 :保留给政府机构使用
                A类IP地址就由1字节的网络地址和3字节主机地址组成,网络地址的最高位必须是“0”
                A类地址范围 1.0.0.1 - 126.255.255.254
        
        B类地址 :分配给中等规模的公司
                B类IP地址就由2字节的网络地址和2字节主机地址组成,网络地址的最高位必须是“10”。
                B类地址范围 128.0.0.1 - 191.255.255.254
        
        C类地址 :分配给任何需要的人
                C类IP地址就由3字节的网络地址和1字节主机地址组成,网络地址的最高位必须是“110”。
                C类地址范围 192.0.0.1 - 223.255.255.254   //192.168.14.2
        
        D类地址 :用于组播
                D类地址范围 224.0.0.1 - 239.255.255.254  //224.0.0.10
        
        E类地址 :用于实验
                E类地址范围 240.0.0.1 - 255.255.255.254

特殊地址:

        每一个字节都为0的地址(“0.0.0.0”)对应于当前主机; INADDR_ANY -->代表当前主机所有的地址
        127.0.0.1  回环地址  --》在当前主机内部自动形成闭环的网络 --》主要用于 主机内部不同的应用程序通信        
                        如果你已经确定当前客户端 和 服务器 都是在同一台主机上运行,那么可以使用这个地址

4、接收端怎么接收组播消息? -->需要加入组播属性的套接字

    #define IP_ADD_MEMBERSHIP    加入组播  
    // usr/include/linux/in.h
    struct ip_mreq  {
        struct in_addr imr_multiaddr;   /* 多播组的IP地址 224.0.0.10/
        struct in_addr imr_interface;   /* 需要加入到多组的IP地址 192.168.53.134 */
    };

5、组播通信的过程

发送端:

1、创建UDP数据报套接字

2、发送数据,往组播地址(224.0.0.10 )里面发送数据

3、关闭 

接收端:(要把接收端的IP地址加入到组播里面)

1、创建UDP数据报套接字

2、定义组播结构体
struct ip_mreq vmreq;

3、设置组播ip(初始化 组播结构体)
inet_pton(AF_INET,"224.0.0.10",&vmreq.imr_multiaddr); // 组播地址
inet_pton(AF_INET,"192.168.14.3",&vmreq.imr_interface); // 需要添加到组的ip


#ininclude <arpa/inet.h>
int inet_pton(int af, const char *src, void *dst);

参数:
    af : 你要选择哪一种协议族  IPV4 --》AF_INET  还是 IPV6--》AF_INET6
    src:本地IP地址
    dst:将本地IP地址转为网络IP地址存储到这里
作用: 
    将本地IP地址转为网络IP地址
    
4)加入组播属性(也就是设置这个套接字 可以接收组播信息)
setsockopt(socketfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&vmreq,sizeof(vmreq));

5)绑定地址
struct sockaddr_in saddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(atoi(argv[1]));
saddr.sin_addr.s_addr = htonl(INADDR_ANY); //htonl(INADDR_ANY)  代表 主机所有的地址

bind(socketfd,(struct sockaddr *)&saddr,sizeof(saddr));

6)接收数据
        
    recvfrom(......)    

6、代码例程

(1)组播发送端

#include<stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>


#define GROUPADDR	"224.0.0.10" //组播地址
#define GROUPPORT	10000

int main()
{
	printf("组播发送端.....\n");
	
	//1、创建UDP数据报套接字
	int socketfd = socket(AF_INET,SOCK_DGRAM,0);
	if(socketfd == -1)
	{
		perror("socket error");
		return -1;
	}
	//2、发送数据,往组播地址(224.0.0.10 )里面发送数据
	struct sockaddr_in sendAddr;//IPV4地址结构体变量
	sendAddr.sin_family = AF_INET;
	sendAddr.sin_port = htons(GROUPPORT);
	sendAddr.sin_addr.s_addr = inet_addr(GROUPADDR);//一定是组播地址
	
	while(1)
	{
		char buf[1024]={0};
		printf("data:");
		scanf("%s",buf); 
		sendto(socketfd,buf,strlen(buf),0,( struct sockaddr *)&sendAddr,sizeof(sendAddr));
	}
	
	
	//3、关闭 
	close(socketfd);
	
	return 0;
}

(2)组播接收端

#include<stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>

#define OWNADDR	"192.168.14.3" //接收端的IP地址   自己ubuntu的IP地址

#define GROUPADDR	"224.0.0.10" //组播地址
#define GROUPPORT	10000

int main()
{
	//1、创建UDP数据报套接字
	int socketfd = socket(AF_INET,SOCK_DGRAM,0);
	if(socketfd == -1)
	{
		perror("socket error");
		return -1;
	}
	//2、定义组播结构体
	struct ip_mreq vmreq;

	//3、设置组播ip(初始化 组播结构体)
	inet_pton(AF_INET,GROUPADDR,&vmreq.imr_multiaddr); // 组播地址
	inet_pton(AF_INET,OWNADDR,&vmreq.imr_interface); // 需要添加到组的ip
	
	//4)加入组播属性(也就是设置这个套接字 可以接收组播信息)
	setsockopt(socketfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&vmreq,sizeof(vmreq));

	//5)绑定地址
	struct sockaddr_in saddr;
	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(GROUPPORT);
	saddr.sin_addr.s_addr = htonl(INADDR_ANY); //htonl(INADDR_ANY)  代表 主机所有的地址

	bind(socketfd,(struct sockaddr *)&saddr,sizeof(saddr));

	//6)接收数据
	struct sockaddr_in otherAddr;
	int len = sizeof(struct sockaddr_in);
	
	while(1)
	{
		char buf[1024]={0};
		
		recvfrom(socketfd,buf,sizeof(buf),0, (struct sockaddr *)&otherAddr,&len);

		printf("来自 %s:%u  recv:%s\n",inet_ntoa(otherAddr.sin_addr),ntohs(otherAddr.sin_port),buf);
	}
	
	//关闭 
	close(socketfd);
	
	return 0;
}

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值