一、广播
1)概念
1.主机之间一对多的通信模式,网络对其中每一台主机发送的数据都进行无条件复制。
2.同属于一个局域网内的所有主机都可以接收到所有广播信息,无论你是否需要。
3.禁止广播数据穿过路由器,防止影响大面积主机,即广播只能做局域网内通信。
4.只有UDP才能广播,因为不需要应答。
5.广播IP:有效网络号+全是1的主机号
192.168.1.2 ----> C类IP地址:网络号前24bit ----> 192.168.1.255
128.1.2.3 ----> B类IP地址:网络号前16bit ----> 128.1.255.255
255.255.255.255:给所有网段中的所有主机发送数据,广播数据禁止穿过路由器,所以只能给当前局域网发送广播数据。
6.同一台主机里面只能有一个接收方,因为接收方需要绑定IP和端口。
2)发送方的流程(类似udp的客户端)
1.socket 创建报式套接字
2.setsockopt 设置允许广播 level:SOL_SOCKET
optname:SO_BROADCAST
3.bind 非必须绑定
4.填充接收方的地址信息结构体,给sendto函数使用
IP:广播IP 与接收方绑定的一致(ps:不能填0.0.0.0)
PORT:1024~49151,与接收方绑定的一致
5.sendto 发送
3)接收方的流程(类似udp的服务器)
1.socket 创建报式套接字
2.填充接收方自身的地址信息结构体,给bind函数使用
①IP:绑定广播IP(192.168.123.255 或者 255.255.255.255 或者0.0.0.0)
0.0.0.0:一旦绑定到套接字上,会将本机所有可用IP地址都绑定到套接字上。例如:
ifconfig出来的本机IP 192.168.122.120
本地环回IP: 127.0.0.1
广播IP
组播IP
②PORT:1024~49151。
3.bind 必须绑定
4.recvfrom 接收数据
4)示例代码
i. 广播的发送方
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <head.h>
#define PORT 6666 //端口号:接收方绑定的端口号
#define IP "192.168.123.255" //IP:接收方绑定的广播IP
int main(int argc, const char *argv[])
{
//创建报式套接字
int cfd = socket(AF_INET, SOCK_DGRAM, 0);
if(cfd < 0)
{
ERR_MSG("socket");
return -1;
}
printf("cfd = %d\n", cfd);
//设置允许广播
int broad = 1;
if(setsockopt(cfd, SOL_SOCKET, SO_BROADCAST, &broad, sizeof(broad)) < 0)
{
ERR_MSG("setsockopt");
return -1;
}
printf("设置允许广播成功\n");
//绑定客户端的地址信息结构体到套接字上---->非必须绑定
//若不绑定,则操作系统会给客户端绑定运行主机的IP和随机端口
//填充接收方的地址信息结构体,给sendto函数使用
//要发给谁,就填谁的地址信息
struct sockaddr_in sin;
sin.sin_family = AF_INET; //必须填AF_INET
sin.sin_port = htons(PORT); //端口号:接收方绑定的端口号
sin.sin_addr.s_addr = inet_addr(IP); //IP:接收方绑定的广播IP
char buf[128] = "";
ssize_t res = 0;
struct sockaddr_in rcvaddr; //存储接收到的数据包从哪里来
socklen_t addrlen = sizeof(rcvaddr);
while(1)
{
bzero(buf, sizeof(buf));
printf("请输入>>> ");
fgets(buf, sizeof(buf), stdin);
buf[strlen(buf)-1] = 0;
//发送数据
if(sendto(cfd, buf, sizeof(buf), 0, (struct sockaddr*)&sin, sizeof(sin)) < 0)
{
ERR_MSG("sendto");
return -1;
}
printf("sendto success\n");
}
//关闭文件描述符
close(cfd);
return 0;
}
ii. 广播的接收方
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <head.h>
#define PORT 6666 //端口号:1024~49151
#define IP "192.168.123.255" //广播IP,ifconfig:broadcast
int main(int argc, const char *argv[])
{
//创建报式套接字
int sfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sfd < 0)
{
ERR_MSG("socket");
return -1;
}
printf("sfd = %d\n", sfd);
//填充接收方自身的地址信息结构体,真实的地址信息结构体根据地址族指定
//AF_INET : man 7 ip
struct sockaddr_in sin;
sin.sin_family = AF_INET; //必须填AF_INET
sin.sin_port = htons(PORT); //端口号:1024~49151
sin.sin_addr.s_addr = inet_addr(IP); //广播IP,ifconfig:broadcast
//绑定接收方的地址信息结构体到套接字上
if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) < 0)
{
ERR_MSG("bind");
return -1;
}
printf("bind success\n");
char buf[128] = "";
ssize_t res = 0;
struct sockaddr_in cin; //存储接收到的数据包从哪里来
socklen_t addrlen = sizeof(cin);
while(1)
{
bzero(buf, sizeof(buf));
//接收数据
res = recvfrom(sfd, buf, sizeof(buf), 0, (struct sockaddr*)&cin, &addrlen);
if(res < 0)
{
ERR_MSG("recvfrom");
return -1;
}
printf("[%s : %d] : %s\n", \
inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), buf);
}
//关闭文件描述符
close(sfd);
return 0;
}
二、组播(多播 多播组)
1)概念
1.广播的方式是给同一网段下的所有主机发送数据,过多的广播会占用大量带宽,影响正常通信
2.主机之间一对一组的通信方式,只有加入了同一个小组的主机可以接收到此组内的所有数据。
3.组播IP:D类IP地址 224.0.0.0-239.255.255.255
2)发送方的流程(类似udp的客户端)
1.socket 创建报式套接字
2.bind 非必须绑定
3.填充接收方的地址信息结构体,给sendto函数使用
IP:组播 与接收方绑定的一致(ps:不能填0.0.0.0)
PORT:1024~49151,与接收方绑定的一致
4.sendto 发送
3)接收方的流程(类似udp的服务器)
1.socket 创建报式套接字
2.setsockopt 加入多播组 level:IPPROTO_IP optname:IP_ADD_MEMBERSHIP
3.填充接收方自身的地址信息结构体,给bind函数使用
①IP:绑定组播IP,与加入的组播一致(224.0.0.0 - 239.255.255.255 或者 0.0.0.0)
0.0.0.0:一旦绑定到套接字上,会将本机所有可用IP地址都绑定到套接字上。例如:
ifconfig出来的本机IP 192.168.122.120
本地环回IP: 127.0.0.1
广播IP
组播IP
②PORT:1024~49151。
4.bind 必须绑定
5.recvfrom 接收数据
4)示例代码
i. 组播的发送方
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <head.h>
#define PORT 6666 //端口号:接收方绑定的端口号
#define IP "224.1.2.3" //组播IP:224.0.0.0-239.255.255.255,与接收方绑定的一致
int main(int argc, const char *argv[])
{
//创建报式套接字
int cfd = socket(AF_INET, SOCK_DGRAM, 0);
if(cfd < 0)
{
ERR_MSG("socket");
return -1;
}
printf("cfd = %d\n", cfd);
//绑定发送方的地址信息结构体到套接字上---->非必须绑定
//若不绑定,则操作系统会给发送方绑定运行主机的IP和随机端口
//填充接收方的地址信息结构体,给sendto函数使用
//要发给谁,就填谁的地址信息
struct sockaddr_in sin;
sin.sin_family = AF_INET; //必须填AF_INET
sin.sin_port = htons(PORT); //端口号:接收方绑定的端口号
sin.sin_addr.s_addr = inet_addr(IP); //组播IP:224.0.0.0-239.255.255.255,与接收方绑定的一致
char buf[128] = "";
ssize_t res = 0;
struct sockaddr_in rcvaddr; //存储接收到的数据包从哪里来
socklen_t addrlen = sizeof(rcvaddr);
while(1)
{
bzero(buf, sizeof(buf));
printf("请输入>>> ");
fgets(buf, sizeof(buf), stdin);
buf[strlen(buf)-1] = 0;
//发送数据
if(sendto(cfd, buf, sizeof(buf), 0, (struct sockaddr*)&sin, sizeof(sin)) < 0)
{
ERR_MSG("sendto");
return -1;
}
printf("sendto success\n");
}
//关闭文件描述符
close(cfd);
return 0;
}
ii. 组播的接收方
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <head.h>
#define PORT 6666 //端口号:1024~49151
#define GRP_IP "224.1.2.3" //接收方需要加入的组播IP 224.0.0.0-239.255.255.255
#define LOL_IP "192.168.122.120" //本机IP ,ifconfig
int main(int argc, const char *argv[])
{
//创建报式套接字
int sfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sfd < 0)
{
ERR_MSG("socket");
return -1;
}
printf("sfd = %d\n", sfd);
//填充需要加入到哪个小组中
struct ip_mreqn mq;
mq.imr_multiaddr.s_addr = inet_addr(GRP_IP); //组播IP,指定要加入的小组,与bind的一致
mq.imr_address.s_addr = inet_addr(LOL_IP); //ifconfig的本机IP
mq.imr_ifindex = 2; //0:自动
//加入多播组
if(setsockopt(sfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mq, sizeof(mq)) < 0)
{
ERR_MSG("setsockopt");
return -1;
}
printf("加入多播组 [%s: %d] 成功\n", GRP_IP, PORT);
//填充接收方自身的地址信息结构体,真实的地址信息结构体根据地址族指定
//AF_INET : man 7 ip
struct sockaddr_in sin;
sin.sin_family = AF_INET; //必须填AF_INET
sin.sin_port = htons(PORT); //端口号:1024~49151
sin.sin_addr.s_addr = inet_addr(GRP_IP); //接收方需要加入的组播IP 224.0.0.0-239.255.255.255
//绑定接收方的地址信息结构体到套接字上
if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) < 0)
{
ERR_MSG("bind");
return -1;
}
printf("bind success\n");
char buf[128] = "";
ssize_t res = 0;
struct sockaddr_in cin; //存储接收到的数据包从哪里来
socklen_t addrlen = sizeof(cin);
while(1)
{
bzero(buf, sizeof(buf));
//接收数据
res = recvfrom(sfd, buf, sizeof(buf), 0, (struct sockaddr*)&cin, &addrlen);
if(res < 0)
{
ERR_MSG("recvfrom");
return -1;
}
printf("[%s : %d] : %s\n", \
inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), buf);
}
//关闭文件描述符
close(sfd);
return 0;
}