一、套接字属性设置相关函数
cpp#include <sys/types.h> /* See NOTES */ #include <sys/socket.h> 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 (man 7 socket) 传输层(TCP):IPPROTO_TCP (man 7 tcp) 传输层(UDP):IPPROTO_UDP (man 7 udp) 网络层:IPPROTO_IP (man 7 ip) 参数3:当前层要设置或获取的属性名称,每层常设置的名称如下表所示: 参数4:要给参数3属性设置的值 参数5:参数4的大小 返回值:成功返回0,失败返回-1并置位错误码
cpp
#include<myhead.h>
int main(int argc, const char *argv[])
{
//创建套接字
int fd = socket(AF_INET, SOCK_DGRAM, 0);
if(fd == -1)
{
perror("socket errorr");
return -1;
}
//想要知道当前套接字能否进行端口号和地址的快速重用
int reuse = -1;
int optlen = sizeof(reuse);
if(getsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, &optlen) ==-1)
{
perror("getsockopt error");
return -1;
}
printf("reuse = %d\n", reuse); // 0,表示套接字默认不允许端口号快重用
//设置端口号快速重用
int optval = 1;
if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) == -1)
{
perror("setsockopt error");
return -1;
}
//再次获取套接字属性的值
reuse = -1;
if(getsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, &optlen) ==-1)
{
perror("getsockopt error");
return -1;
}
printf("reuse = %d\n", reuse); // 1,表示已经设置端口号快速重用了
return 0;
}
二、单播
就是实现一对一的通信,我们之前所学的TCP或UDP通信方式都是使用的是单播
发送端和接收端是确定的
三、多点通信
1> 实现主机之间的一对多的通信,一个发送端可以对应多个接收端
2> 对于接收端而言,无论愿不愿意接收,都会收到消息
3> 只能基于UDP实现多点通信
4> 实现多点通信的方式:广播、组播
3.1 广播
1> 能够实现主机的一对多的通信
2> 在当前网络下的所有主机都能接收到广播消息
3> 对于广播的发送端套接字而言,需要将其设置成允许广播的状态
4> 广播地址:网络号 + 255
5> 广播消息不允许穿过路由器
6> 广播分为发送端和接收端
7> 广播的发送端流程----> 类似于UDP的客户端
cpp
1、socket:创建用于通信的套接字文件描述符
2、setsockopt:设置当前套接字允许广播, level:SOL_SOCKET optname:SO_BROADCAST optval: int
3、bind:可绑定也可以不绑定
4、发送广播消息:sendto
目的IP地址:广播地址
目的Port:与接收端保持一致
5、close:关闭套接字
cpp
#include<myhead.h>
#define IP "192.168.125.255" //广播地址
#define PORT 6789 //端口号
int main(int argc, const char *argv[])
{
//1、创建套接字
int sfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sfd == -1)
{
perror("socket error");
return -1;
}
//2、设置套接字允许广播
int broadcast = 1;
if(setsockopt(sfd, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof(broadcast)) == -1)
{
perror("setsockopt error");
return -1;
}
//3、发送消息
//3.1 填充对端地址信息结构体
struct sockaddr_in rin;
rin.sin_family = AF_INET;
rin.sin_port = htons(PORT);
rin.sin_addr.s_addr = inet_addr(IP);
char sbuf[128] = "";
while(1)
{
//从终端获取数据
printf("请输入>>>");
fgets(sbuf, sizeof(sbuf), stdin);
sbuf[strlen(sbuf)-1] = 0;
//将数据广播出去
sendto(sfd, sbuf, strlen(sbuf), 0, (struct sockaddr*)&rin, sizeof(rin));
printf("发送成功\n");
}
//4、关闭套接字
close(sfd);
return 0;
}
8> 广播的接收端流程 ----> 类似于UDP的服务器端
1、socket:创建用于通信的套接字文件描述符 2、bind:必须绑定 IP地址:广播地址 Port:与发送端保持一致 3、recvfrom:接收广播消息 4、close:关闭套接字
cpp
#include<myhead.h>
#define IP "192.168.125.255" //广播地址
#define PORT 6789 //端口号
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和端口号
//2.1 填充地址信息结构体
struct sockaddr_in rin;
rin.sin_family = AF_INET;
rin.sin_port = htons(PORT);
rin.sin_addr.s_addr = inet_addr(IP);
//2.2 绑定
if(bind(rfd, (struct sockaddr*)&rin, sizeof(rin)) == -1)
{
perror("bind error");
return -1;
}
//3、接受消息
char rbuf[128] = "";
while(1)
{
bzero(rbuf, sizeof(rbuf)); //清空容器
//读取数据
recvfrom(rfd, rbuf, sizeof(rbuf), 0, NULL, NULL);
//recv(rfd, rbuf, sizeof(rbuf), 0);
printf("收到广播消息:%s\n", rbuf);
}
//4、关闭套接字
close(rfd);
return 0;
}
3.2 组播
1> 组播也是实现一对多的通信
2> 组播地址:D类网络---> [224.0.0.0 -- 239.255.255.255]
3> 组播也分为发送端和接收端,对于接收端而言,要加入多播组后,才能收到组播消息
4> 组播的发送端 ----> 类似于UDP的客户端
cpp
1、socket:创建用于通信的套接字文件描述符
2、bind:可绑定也可以不绑定
3、发送广播消息:sendto
目的IP地址:组播地址
目的Port:与接收端保持一致
4、close:关闭套接字
cpp
#include<myhead.h>
int main(int argc, const char *argv[])
{
//1、创建套接字
int sfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sfd == -1)
{
perror("socket error");
return -1;
}
//3、发送消息
//3.1 填充对端地址信息结构体
struct sockaddr_in rin;
rin.sin_family = AF_INET;
rin.sin_port = htons(5555);
rin.sin_addr.s_addr = inet_addr("224.1.1.1"); //组播地址
char sbuf[128] = "";
while(1)
{
//从终端获取数据
printf("请输入>>>");
fgets(sbuf, sizeof(sbuf), stdin);
sbuf[strlen(sbuf)-1] = 0;
//将数据广播出去
sendto(sfd, sbuf, strlen(sbuf), 0, (struct sockaddr*)&rin, sizeof(rin));
printf("发送成功\n");
}
//4、关闭套接字
close(sfd);
return 0;
}
5> 组播的接收端 ----> 类似于UDP的服务器端
cpp
1、socket:创建用于通信的套接字文件描述符
2、setsockopt:将当前套接字加入多播组, level:IPPROTO_IP optname:IP_ADD_MEMBERSHIP optval:struct ip_mreq
struct ip_mreqn {
struct in_addr imr_multiaddr; //广播地址
struct in_addr imr_address; //主机ip
int imr_ifindex; //网卡编号 可以通过指令 ip ad 进行查看当前网卡设备的编号
};
3、bind:必须绑定
IP地址:广播地址
Port:与发送端保持一致
4、recvfrom:接收广播消息
5、close:关闭套接字
cpp
#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、加入多播组
//2.1 创建结构体变量
struct ip_mreqn imq;
imq.imr_multiaddr.s_addr = inet_addr("224.1.1.1"); //广播地址
imq.imr_address.s_addr = inet_addr("192.168.125.221"); //主机IP
imq.imr_ifindex = 2; //网卡索引
//2.2 设置套接字
if(setsockopt(rfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imq, sizeof(imq)) == -1)
{
perror("setsockopt error");
return -1;
}
printf("加入多播组成功\n");
//3、绑定
//3.1 填充地址信息结构体
struct sockaddr_in rin;
rin.sin_family = AF_INET;
rin.sin_port = htons(5555); //端口号
rin.sin_addr.s_addr = inet_addr("224.1.1.1"); //组播IP
// 3.3绑定
if(bind(rfd, (struct sockaddr*)&rin, sizeof(rin)) == -1)
{
perror("bind error");
return -1;
}
//4、接受消息
char rbuf[128] = "";
while(1)
{
bzero(rbuf, sizeof(rbuf)); //清空容器
//读取数据
recvfrom(rfd, rbuf, sizeof(rbuf), 0, NULL, NULL);
//recv(rfd, rbuf, sizeof(rbuf), 0);
printf("收到组播消息:%s\n", rbuf);
}
//5、关闭套接字
close(rfd);
return 0;
}