一、网络属性(getsockopt、setsockopt)
1> 由于在网络通信过程中,套接字是服务于各个层的,但是,每一层中对套接字选项都有一定的权限控制,例如,应用层中对端口号快速重用的限制
2> 如何更改这些限制,可以通过上述两个函数完成,分别是获取套接字属性,以及设置套接字属性
3> 函数原型
#include <sys/types.h> /* See NOTES */#include <sys/socket.h>int getsockopt(int sockfd, int level, int optname,void *optval, socklen_t *optlen);功能:获取套接字对应层中相关属性参数1:套接字文件描述符参数2:网络体系结构的层SOL_SOCKET:应用层IPPROTO_TCP:tcp套接字的应用层IPPROTO_UDP:udp套接字的应用层IPPROTO_IP:网络层参数3:当前层中的指定属性,是一个宏值参数4:用于存储获取下来的当前属性的容器起始地址参数5:参数4的大小返回值:成功返回0,失败返回-1并置位错误码int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);功能:设置套接字对应层中相关属性参数1:套接字文件描述符参数2:网络体系结构的层SOL_SOCKET:应用层IPPROTO_TCP:tcp套接字的应用层IPPROTO_UDP:udp套接字的应用层IPPROTO_IP:网络层参数3:当前层中的指定属性,是一个宏值参数4:设置当前套接字选项的容器的起始地址,一般为int类型参数5:参数4的大小返回值:成功返回0,失败返回-1并置位错误码
#include<myhead.h>int main(int argc, const char *argv[])
{//创建套接字int sfd = socket(AF_INET, SOCK_STREAM, 0);if(sfd == -1){perror("socket error");return -1;}//获取当前套接字选项,查看是否默认允许端口号快速重用int reuse = -1;int reuselen = sizeof(reuse);//调用函数获取套接字属性if(getsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, &reuselen) == -1){perror("getsockopt error");return -1;}printf("reuse = %d\n", reuse); //0//将端口号快速重用属性设置为启用int value = -1;if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value))==-1){perror("setsockopt error");return -1;}//调用函数获取套接字属性if(getsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, &reuselen) == -1){perror("getsockopt error");return -1;}printf("reuse = %d\n", reuse); //1return 0;
}
二、单播
数据从一个发送者到一个接收者的数据传输方式叫做单播,之前学习的无论是TCP还是UDP通信都属于单播
三、广播
3.1 概念
1> 顾名思义就是实现一对多的通信,一个发送者可以对应对个接收者
2> 所有的接受者都能收到发送者发送的消息,无论接受者愿不愿意接受
3> 广播只能使用UDP实现
4> 广播地址:当前网络中主机号全为1的ip地址就是广播地址,例如:192.168.125.255
5> 广播消息不能穿过路由器
6> 广播需要对广播的发送者设置允许广播属性
3.2 广播的发送端流程 ---> 类似于UDP的客户端流程
1> socket:创建用于通信的套接字
2> setsockopt:设置允许广播,level:SOL_SOCKET, optname:SO_BROADCAST,属性类型:int
3> bind:非必须
4> 填充接收端地址信息结构体
ip:广播地址
port:和接收端保持一致
5> sendto:发送数据
6> close:关闭套接字
#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;}//2、设置当前套接字允许广播属性int broadcast = 1; if(setsockopt(sfd, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof(broadcast))==-1){perror("setsockopt error");return -1;}//3、绑定(可选)//4、填充接收端地址信息结构体struct sockaddr_in sin;sin.sin_family = AF_INET;sin.sin_port = htons(8888);sin.sin_addr.s_addr = inet_addr("192.168.125.255");//5、发送数据char sbuf[128] = "";while(1){printf("请输入>>>");fgets(sbuf, sizeof(sbuf), stdin);sbuf[strlen(sbuf)-1] = 0;sendto(sfd, sbuf, strlen(sbuf), 0, (struct sockaddr*)&sin, sizeof(sin));printf("发送成功\n");}//6、关闭close(sfd);return 0;
}
3.3 广播的接收端流程 ---> 类似UDP的服务器端流程
1> socket:创建套接字
2> 填充地址信息结构体
ip:广播地址
port:与发送端保持一致
3> bind:必须
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;}printf("rfd = %d\n", rfd); //3//2、填充地址信息结构体struct sockaddr_in rin;rin.sin_family = AF_INET; //地址族rin.sin_port = htons(8888); //端口号rin.sin_addr.s_addr = inet_addr("192.168.125.255"); //广播地址//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));//读取消息recv(rfd, rbuf, sizeof(rbuf), 0);printf("收到消息:%s\n", rbuf);}//5、关闭套接字close(rfd);return 0;
}
四、组播
4.1 概念
1> 组播也是实现同一网络下的一对多的通信方式
2> 对加入多播组的所有成员才能收到组播消息,并不是所有网络下的成员都能收到
3> 对于接收端需要进行设置,加入多播组,发送端无需特殊设置
4> 组播IP地址:D类网络地址【224.0.0.0 --- 239.255.255.255】
4.2 组播的发送端流程 ---> 类似于UDP的客户端流程
1> socket:创建套接字
2> bind:非必须
3> 填充地址信息结构体
ip:D类网络
port:与接收端保持一致
4> sendto:发送数据
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地址//填充地址信息结构体struct sockaddr_in rin;rin.sin_family = AF_INET;rin.sin_port = htons(9999);rin.sin_addr.s_addr = inet_addr("192.168.125.205");//绑定端口号和IPif(bind(rfd, (struct sockaddr*)&rin, sizeof(rin)) == -1){perror("bind error");return -1;}//3、发送消息//填充接收端地址信息结构体struct sockaddr_in sin;sin.sin_family = AF_INET;sin.sin_port = htons(8888);sin.sin_addr.s_addr = inet_addr("224.1.2.3");char sbuf[128] = "";while(1){printf("请输入>>>");fgets(sbuf, sizeof(sbuf), stdin);sbuf[strlen(sbuf)-1] = 0;//向组播地址中发送消息sendto(rfd, sbuf, strlen(sbuf), 0, (struct sockaddr*)&sin, sizeof(sin));printf("发送成功\n");}//4、关闭close(rfd);return 0;
}
4.3 组播的接收端流程 ---> 类似于UDP的服务器端流程
1> socket:创建套接字
2> setsockopt:加入多播组,level:IPPROTO_IP,optname:IP_ADD_MEMBERSHIP,类型:struct
struct ip_mreqn {struct in_addr imr_multiaddr; /* IP multicast groupaddress */ 组播地址struct in_addr imr_address; /* IP address of localinterface */ 主机IP地址int imr_ifindex; /* interface index */ 网卡接口索引 ifconfig可以查看网卡名称, ip ad查看网卡对应的编号索引也可以天0,表示系统默认选择};
3> bind:绑定
ip:组播IP
port:与接收端保持一致
4> sendto:发送数据
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;}printf("rfd = %d\n", rfd); //3//设置加入多播族struct ip_mreqn imr;imr.imr_multiaddr.s_addr = inet_addr("224.1.2.3"); //组播ipimr.imr_address.s_addr = inet_addr("192.168.125.205"); //本地ipimr.imr_ifindex = 2; //网卡编号if(setsockopt(rfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr)) == -1){perror("setsockopt error");return -1;}//2、填充地址信息结构体struct sockaddr_in rin;rin.sin_family = AF_INET; //地址族rin.sin_port = htons(8888); //端口号rin.sin_addr.s_addr = inet_addr("224.1.2.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));//读取消息recv(rfd, rbuf, sizeof(rbuf), 0);printf("收到消息:%s\n", rbuf);}//5、关闭套接字close(rfd);return 0;
}