1、套接字选项(socket options)
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);
功能:获取或设置套接字在不同层级上的相关属性
参数:
sockfd: 要获取或设置的套接字文件描述符
level: 表示要获取或设置的层
应用层 SOL_SOCKET
传输层 IPPROTO_TCP IPPROTO_UDP
网络层 IPPROTO_IP
optname:当前层的数下名称见下表
optval: 要获取或设置的属性值的起始地址
optlen: optval的大小
返回值:成功 0 失败 -1 并重置错误码
快速重用地址
int reuse = 1; if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))==-1) { perror("setsockopt error"); return -1; }
2、单播
1、主机之间存在的是一对一的通信方式,交换机以及路由器对数据只进行转发,不进行复制
2、每次只有两个实体之间进行相互通信,发送端和接收端都是唯一确定的
3、广播和组播的区别
组播是一种多对多的通信方式,其中消息被发送到一个特定的组播地址,只有加入该组播地址的主机才能接收到这个消息。组播允许发送方只向特定的一组接收方发送消息,可以在节省带宽的同时实现多对多通信,广泛应用于IP电话、视频会议、多媒体流等场景。
广播是一种发送消息给所有主机的通信方式。在广播中,消息被发送到一个广播地址,该地址会被所有网络中的主机接收。广播是一种无条件的发送方式,因此它常常被用于网络中一些需要向所有主机广播消息的场景,例如DHCP自动分配IP地址和ARP协议中解析MAC地址等。
总结:组播和广播的主要区别在于它们【发送消息的方式】和目【标接收者的范围不同】。广播是一种无条件的发送方式,消息被发送到所有主机;而组播则是有条件的发送方式,消息只会被发送到加入该组播地址的特定主机。
4、广播
1、广播是允许通信实体之间完成一对多的通信方式,网络对其中每一台发送数据的主机信息都进行无条件的复制后并转发给其他所有主机
2、所有的主机都会收到广播消息(不管是否愿意接收)
3、广播只能由UDP通信方式来实现
4、广播消息,只能在当前网络中进行传播,不允许传过路由器
5、广播地址:网络号 + 255
6、广播分为发送端和接收端实现
3.1 广播的发送端------->类似于UDP的客户端
socket 创建用于通信的套接字文件描述符
setsockopt (SOL_SOCKET SOBROADCAST ) 设置该套接字允许发送广播消息
bind 绑定IP地址和端口号(可选)
sendto 向广播地址发送数据
广播地址IP:网络号 + 255
端口号:与接收端保持一致
close 关闭套接字
int main(){ //1、创建用于通信的套接字文件描述符:socket int sfd = socket(AF_INET, SOCK_DGRAM, 0); if(sfd == -1) { perror("socket error"); return -1; } //2、设置该套接字允许发送广播消息:setsockopt ---> SOL_SOCKET //---> SO_BROADCAST int broadcast = 1; if(setsockopt(sfd, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof(broadcast)) ==-1) { perror("setsockopt error"); return -1; } //3、绑定IP地址和端口号(可选) //4、向广播地址发送数据:sendto // 广播地址IP:网络号 + 255 // 端口号:与接收端保持一致 struct sockaddr_in rin; rin.sin_family = AF_INET; rin.sin_port = htons(6666); //跟接收端保持一致 rin.sin_addr.s_addr = inet_addr("xxx.xxx.xxx.255"); //发送数据 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; }
3.2 广播的接收端------->类似于UDP的服务器端
1、创建用于通信的套接字文件描述符
2、填充地址信息结构体:
ip:广播地址
port:与发送端保持一致
3、绑定端口号和IP地址(必须)
4、接受发送端发来的消息:recvfrom
5、关闭套接字:close
int main(){ //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("xxx.xxx.xxx.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; }
5、组播
1、由于广播通信过程中,会占用大量的网络带宽,影响正常通信,如果通信的主机较少的话,可以引入组播
2、组播也是实现主机之间一对多的通信方式
3、要求所有接受端主机加入多播组,只有加入多播组的主机才能收到消息
4、对于发送端而言,只需向该多播组中发送消息即可,无需其他设置
5、组播也是使用UDP实现的
6、组播地址:D类网络 【224.0.0.0 --- 239.255.255.255】
7、组播也分为发送端和接收端
4.1 组播的发送端 ---> 类似于UDP的客户端流程
1、创建用于通信的套接字文件描述符:socket
2、绑定IP地址和端口号(可选)
3、向组播地址发送数据:sendto
广播地址IP:【224.0.0.0 --- 239.255.255.255】
端口号:与接收端保持一致
4、关闭套接字:close
int main() { //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; }
4.2 组播的接收端流程 ---> 类似于UDP的服务器端流程
1、创建用于通信的套接字文件描述符:socket
2、将该套接字加入多播组:
setsockopt ---> IPPROTO_IP --> IP_ADD_MEMBERSHIP
属性类型:
#include <netinet/in.h>
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
int main() { //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("xxx.xxx.xxx.xxx");//本机主机ip地址 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; }