一、广播
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;
}