目录
前言
二、多点通信
2.1 单播
2.2 广播
2.2.1 广播得发送端实现--》类似与UDP的客户端
2.3 组播
2.3.1 组播发送端流程--》类似于UDP的客户端流程
2.3.2 组播的接收端流程---》类似于UDP的服务器端流程
前言
多点通信
一、套接字选项得获取和设置
int getsockopt(int sockfd, int level, int optname,void *optval, socklen_t *optlen);
int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);
功能:获取或设置套接字在不同层级上的相关属性
参数1:要获取或设置的套接字文件描述符
参数2:表示要获取或设置的层
应用层:SOL_SOCKET
传输层TCP:IPPROTO_TCP
传输层UDP:IPPROTO_UDP
网络层:IPPROTO_IP
参数3:当前层的属性名称,见下表所示
参数4:要获取或设置的属性值的起始地址
参数5:参数4的大小
返回值:成功返回0,失败返回-1并置位错误码
1.代码演示
#include<myhead.h>
int main(int argc, const char *argv[])
{
//1、创建一个套接字文件描述符
int sfd = socket(AF_INET, SOCK_STREAM, 0);
if(sfd == -1)
{
perror("socket error");
return -1;
}
//2、获取地址快速重用属性的值
int res = -1; //接收属性值
int reslen = sizeof(res); //接收属性的大小
if(getsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &res, &reslen)==-1)
{
perror("getsockopt error");
return -1;
}
//程序执行至此,就获取了默认是否能地址快速重用
printf("res = %d\n", res); //0表示默认不能快速重用
//3\、设置地址能够快速重用
int reuse = 1;
if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))==-1)
{
perror("setsockopt error");
return -1;
}
//4、再重新获取一次,判断是否设置成功
res = -1;
if(getsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &res, &reslen)==-1)
{
perror("getsockopt error");
return -1;
}
//程序执行至此,就获取了默认是否能地址快速重用
printf("res = %d\n", res); //1表示已经设置成功了
return 0;
}
二、多点通信
2.1 单播
1.主机之间存在的是一对一的通信方式,交换机以及路由器对数据只进行转发,不进行复制
2.每次只有两个实体之间进行相互通信,发送端和接收端都是唯一确定的
2.2 广播
1.广播是允许通信实体之间完成一对多的通信方式,网络对其中每一台发送数据的主机信息都进行无条件的复制后并转发给其他所有主机
2.所有主机都会收到广播消息(不管是都愿意接受)
3.广播只能由UDP通信方式来实现
4.广播消息,只能在当前网络下进行转播,不允许传过路由器
5.广播地址:网络号+255
6.官博分为发送端和接收端实现
2.2.1 广播得发送端实现--》类似与UDP的客户端
1、创建用于通信的套接字文件描述符:socket
2、设置该套接字允许发送广播消息:setsockopt ---> SOL_SOCKET ---> SO_BROADCAST
3、绑定IP地址和端口号(可选)
4、向广播地址发送数据:sendto
广播地址IP:网络号 + 255
端口号:与接收端保持一致
5、关闭套接字:close
代码实现:
#include<myhead.h>
int main(int argc, const char *argv[])
{
//1、创建用于通信的套接字文件描述符
int rfd = socket(AF_INET, SOCK_DGRAM, 0);
if(rfd == -1)
{
perror("socket error");
return -1;
}
//2、填充地址信息结构体:
//ip:广播地址
//port:与发送端保持一致
struct sockaddr_in rin;
rin.sin_family = AF_INET;
rin.sin_port = htons(6666); //端口号
rin.sin_addr.s_addr = inet_addr("192.168.125.255"); //广播ip地址
//3、绑定端口号和IP地址(必须)
if(bind(rfd, (struct sockaddr*)&rin, sizeof(rin)) == -1)
{
perror("bind error");
return -1;
}
//4、接受发送端发来的消息:recvfrom
char rbuf[128] = "";
while(1)
{
//清空容器
bzero(rbuf, sizeof(rbuf));
//读取数据
recv(rfd, rbuf, sizeof(rbuf), 0);
//展示
printf("收到广播消息:%s\n", rbuf);
//判断广播是否结束
if(strcmp(rbuf, "over") == 0)
{
break;
}
}
//5、关闭套接字:close
close(rfd);
return 0;
}
2.2.2 广播的接收端--》类似于UDP的服务器端
1、创建用于通信的套接字文件描述符
2、填充地址信息结构体:
ip:广播地址
port:与发送端保持一致
3、绑定端口号和IP地址(必须)
4、接受发送端发来的消息:recvfrom
5、关闭套接字:close
代码实现:
#include<myhead.h>
int main(int argc, const char *argv[])
{
//1、创建用于通信的套接字文件描述符
int rfd = socket(AF_INET, SOCK_DGRAM, 0);
if(rfd == -1)
{
perror("socket error");
return -1;
}
//2、填充地址信息结构体:
//ip:广播地址
//port:与发送端保持一致
struct sockaddr_in rin;
rin.sin_family = AF_INET;
rin.sin_port = htons(6666); //端口号
rin.sin_addr.s_addr = inet_addr("192.168.125.255"); //广播ip地址
//3、绑定端口号和IP地址(必须)
if(bind(rfd, (struct sockaddr*)&rin, sizeof(rin)) == -1)
{
perror("bind error");
return -1;
}
//4、接受发送端发来的消息:recvfrom
char rbuf[128] = "";
while(1)
{
//清空容器
bzero(rbuf, sizeof(rbuf));
//读取数据
recv(rfd, rbuf, sizeof(rbuf), 0);
//展示
printf("收到广播消息:%s\n", rbuf);
//判断广播是否结束
if(strcmp(rbuf, "over") == 0)
{
break;
}
}
//5、关闭套接字:close
close(rfd);
return 0;
}
2.3 组播
1.由于广播通信过程中,会占用大量的网络宽带,影响正常通信,如果通信的主机较少的话,可以引入组播
2.组播也是实现主机之间一对多的通信方式
3.要求所有接收端主机加入多播组,只有加入多播组的主机才能收到消息
4.对于发送端而言,只需向该多组播中发送消息即可,无需其他设置
5.组播也是使用UDP实现的
6.组播地址:D类网络【224.0.0.0---239.255.255.255】
7.组播也分为发送端和接收端
2.3.1 组播发送端流程--》类似于UDP的客户端流程
1、创建用于通信的套接字文件描述符:socket
2、绑定IP地址和端口号(可选)
3、向组播地址发送数据:sendto
广播地址IP:【224.0.0.0 --- 239.255.255.255】
端口号:与接收端保持一致
4、关闭套接字:close
代码实现:
#include<myhead.h>
int main(int argc, const char *argv[])
{
//1、创建用于通信的套接字文件描述符:socket
int sfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sfd == -1)
{
perror("socket error");
return -1;
}
//2、绑定IP地址和端口号(可选)
//3、向组播地址发送数据:sendto
//广播地址IP:【224.0.0.0 --- 239.255.255.255】
//端口号:与接收端保持一致
struct sockaddr_in rin;
rin.sin_family = AF_INET;
rin.sin_port = htons(5555);
rin.sin_addr.s_addr = inet_addr("224.1.2.3");
//数据的发送
//发送数据
char wbuf[128] = "";
while(1)
{
//从终端获取数据
fgets(wbuf, sizeof(wbuf), stdin);
wbuf[strlen(wbuf)-1] = 0;
//发送给广播消息
sendto(sfd, wbuf, strlen(wbuf), 0, (struct sockaddr*)&rin, sizeof(rin));
printf("发送成功\n");
//判断发送的消息
if(strcmp(wbuf, "over") == 0)
{
break;
}
}
//5、关闭套接字:close
close(sfd);
return 0;
}
2.3.2 组播的接收端流程---》类似于UDP的服务器端流程
1、创建用于通信的套接字文件描述符:socket
2、将该套接字加入多播组:setsockopt ---> IPPROTO_IP --> IP_ADD_MEMBERSHIP
属性类型:
struct ip_mreqn {
struct in_addr imr_multiaddr; /* 组播地址 */
struct in_addr imr_address; /* 主机ip地址 */
int imr_ifindex; /* 设备索引,可以通过 ip ad指令查看, 也可以填0,表示使用默认的索引*/
};
3、填充地址信息结构体:
ip:组播地址
port:与发送端保持一致
4、绑定端口号和IP地址(必须)
5、接受发送端发来的消息:recvfrom
6、关闭套接字:close
代码实现:
#include<myhead.h>
int main(int argc, const char *argv[])
{
//1、创建用于通信的套接字文件描述符:socket
int rfd = socket(AF_INET, SOCK_DGRAM, 0);
if(rfd == -1)
{
perror("socket error");
return -1;
}
//2、将该套接字加入多播组:setsockopt ---> IPPROTO_IP --> IP_ADD_MEMBERSHIP
struct ip_mreqn imr;
imr.imr_multiaddr.s_addr = inet_addr("224.1.2.3");
imr.imr_address.s_addr = inet_addr("192.168.125.113");
imr.imr_ifindex = 2;
//设置套接字选项,加入多播组
if(setsockopt(rfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr))==-1)
{
perror("setsockopt error");
return -1;
}
//3、填充地址信息结构体:
//ip:组播地址
//port:与发送端保持一致
struct sockaddr_in rin;
rin.sin_family = AF_INET;
rin.sin_addr.s_addr = inet_addr("224.1.2.3");
rin.sin_port = htons(5555);
//4、绑定端口号和IP地址(必须)
if(bind(rfd, (struct sockaddr*)&rin, sizeof(rin)) == -1)
{
perror("bind error");
return -1;
}
//5、接受发送端发来的消息:recvfrom
char rbuf[128] = "";
while(1)
{
//清空容器
bzero(rbuf, sizeof(rbuf));
//读取数据
recv(rfd, rbuf, sizeof(rbuf), 0);
//展示
printf("收到广播消息:%s\n", rbuf);
//判断广播是否结束
if(strcmp(rbuf, "over") == 0)
{
break;
}
}
//6、关闭套接字:close
close(rfd);
return 0;
}