#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:要获取的层级
参数3:要获取的操作名称
参数4:获取的值,0:表示禁用,非0表示启用。
参数5:参数4的大小。
返回值:成功返回0,失败返回-1,并置位错误码
int setsockopt(int sockfd, int level, int optname,
const void *optval, socklen_t optlen);
功能:设置套接字某些选项的属性。
参数1:套接字描述符
参数2:要设置的层级
参数3:要设置的操作名称
参数4:设置的值,0:表示禁用,非0表示启用。
参数5:参数4的大小。
返回值:成功返回0,失败返回-1,并置位错误码
eg:获取当前端口号是否能快速复用属性:
int n;
int len = sizeof(n);
getsockopt(oldfd,SOL_SOCKET,SO_REUSEADDR,&n,&len);
如果n==0表示端口号快速复用未启动。
如果n!=0表示端口号快速复用启动。
eg:设置当前套接字端口号能快速复用
int n =999;
setsockopt(oldfd,SOL_SOCKET,SO_REUSEADDR,&n,sizeof(n));启动端口号快速复用功能。
#include <myhead.h>
int main(int argc, const char *argv[])
{
int oldfd = socket(AF_INET,SOCK_STREAM,0);
int n;
int len = sizeof(n);
getsockopt(oldfd,SOL_SOCKET,SO_REUSEADDR,&n,&len);
printf("端口号复用状态:%d\n",n);
n = 1;
setsockopt(oldfd,SOL_SOCKET,SO_REUSEADDR,&n,sizeof(n));
printf("端口号修改为可复用状态后:%d\n",n);
return 0;
}
1.单播:
1.单播发生在主机之间一对一的通信模式,交换机或者路由器只对数据进行转发,不做复制
2.每次只有两个实体之间进行相互通信,发送端和接收端都是唯一确定的
2.广播
1> 主机之间的一对多的通信模式,网络对其中的每一台主机发出的信息都进行复制并转发
2> 所有主机都可以收到广播消息(无论你是否愿意接收),所以,广播是基于UDP通信模式
3> 广播地址:网络号 + 255
例如:主机地址为192.168.125.171 ---> 192.168.125.255
4> 广播消息是不能穿过路由器的,也就是说广播消息禁止在外网上进行传播,所以广播只能完成局域网内的多点通信
1、广播的发送端模型 ----> 类似于UDP的客户端
1> socket 创建套接字
2> setsockopt 设置网络属性,允许广播
3> bind 非必须绑定(绑定的话每次发送端口都是固定的)
4> 填广播地址信息结构体
ip:填广播地址(192.168.125.255)
port:与接收端保持一致
5> sendto 发送消息
6> close 关闭套接字
#include <luochen.h>
#define PORT 6666
#define IP "192.168.124.255"
int main(int argc, const char *argv[])
{
//1、创建套接字
int oldfd = socket(AF_INET,SOCK_DGRAM,0);
if(oldfd==-1)
{
perror("socket");
return -1;
}
//2、设置允许广播
int k = 999;
if(setsockopt(oldfd,SOL_SOCKET,SO_BROADCAST,&k,sizeof(k))==-1)
{
perror("setsockopt");
return -1;
}
printf("允许广播设置成功\n");
struct sockaddr_in broadcast = {
.sin_family = AF_INET,
.sin_port = htons(PORT),//发送到该端口
.sin_addr.s_addr = inet_addr(IP)
};
char buff[1024];
while(1)
{
fgets(buff,sizeof(buff),stdin);
sendto(oldfd,buff,sizeof(buff),0,(struct sockaddr *)&broadcast,sizeof(broadcast));
if(strcmp(buff,"quit")==0)
{
printf("发送端退出\n");
break;
}
}
close(oldfd);
return 0;
}
2、广播的接收端模型 ----> 类似于UDP的服务器端
1> socket 创建套接字
2> 填充地址信息结构体
ip:广播地址(192.168.125.255)
port:与发送端保持一致
3> bind 绑定端口号与ip地址
4> recvfrom 接收消息
5> close 关闭套接字
特点:
1、广播地址选取broadcast对应的IP
2、端口号固定
3、发送端不需要绑定固定的IP和端口号,只需要向固定的IP和端口号(广播地址)发送信息即可,
不需要关注谁来接收。
4、接收端必须绑定IP和端口号(广播地址)
5、如果发送端绑定了IP和端口号之后,接收端就无法再次绑定,也无法接收信息。
#include <myhead.h>
#define IP "192.168.124.255"
#define PORT 6666
int main(int argc, const char *argv[])
{
//1、创建套接字
int oldfd = socket(AF_INET,SOCK_DGRAM,0);
if(oldfd==-1)
{
perror("socket");
return -1;
}
//2、填充广播地址信息结构体
struct sockaddr_in send = {
.sin_family = AF_INET,
.sin_port = htons(PORT),
.sin_addr.s_addr = inet_addr(IP)
};
#if 1
if(bind(oldfd,(struct sockaddr *)&send,sizeof(send))==-1)
{
perror("bind");
return -1;
}
#endif
//接收消息
char buff[1024];
while(1)
{
recvfrom(oldfd,buff,sizeof(buff),0,NULL,NULL);
printf("%s",buff);
}
return 0;
}
3、组播(多播)
1、 组播也是实现主机之间一对多的通信模型,跟广播不同的是,组播发送的消息,只有加入多播组的成员才能收到,没有加入的就无法收到,不会占用柜台的网络带宽。
2> 组播也是使用UDP实现。
3> 组播地址:就是D类网络,224.0.0.0 -- 239.255.255.255
2、 组播的发送端模型 --->类似于UDP的客户端
1> socket 创建套接字
2> bind 非必须绑定
3> 填充接收端地址信息结构体
ip:组播地址,与接收端保持一致(224.0.0.0 -- 239.255.255.255)
port:与接收端保持一致
4> sendto 发送组播消息
5> close 关闭套接字
组播发送端:
特点:
1、发送端发送给组播组IP和端口号。
2、发送端不需要绑定自己的IP和端口号,只需要发送到组播地址即可。
#include <myhead.h>
#define PORT 6666
#define IP "192.168.124.172"
int main(int argc, const char *argv[])
{
//1、创建套接字
int oldfd = socket(AF_INET,SOCK_DGRAM,0);
if(oldfd==-1)
{
perror("socket");
return -1;
}
#if 0
//2、绑定
struct sockaddr_in send = {
.sin_family = AF_INET,
.sin_port = htons(PORT),
.sin_addr.s_addr = inet_addr(IP)
};
if(bind(oldfd,(struct sockaddr *)&send,sizeof(send))==-1)
{
perror("bind");
return -1;
}
#endif
//3、发送接收
struct sockaddr_in group = {
.sin_family = AF_INET,
.sin_port = htons(8888),//组播端口号
.sin_addr.s_addr = inet_addr("224.1.2.3")//组播IP地址
};
char buff[1024];
while(1)
{
fgets(buff,sizeof(buff),stdin);
buff[strlen(buff)-1] = '\0';
sendto(oldfd,buff,sizeof(buff),0,(struct sockaddr *)&group,sizeof(group));
}
return 0;
}
3、组播的接收端模型 ---> 类似于UDP的服务器
1> socket 创建套接字
2> setsockopt 设置网络属性(加入多播组)
设置层级:IPPROTO_IP
设置属性:IP_ADD_MEMBERSHIP
3> 填充地址信息结构体然后bind
ip:组播IP,与发送端保持一致
port :与发送端保持一致
4> 定义发送方结构体,接收发送方信息
5> recvfrom 接收消息
6> close 关闭套接字
特点:
1、设置允许加入组播组(发送端的IP),设置加入组播组时需要(组播组IP,自己的IP,自己网卡ens33的索引号一般都是2)
2、查找自己网卡的索引号:ip addr show或者ip ad。
3、绑定时绑定的是组播组IP和发送端IP保持一致。
4、接收端也可以选择不接收发送方信息,recvfron最后两个参数填NULL即可。
#include <myhead.h>
#define ZIP "224.1.2.3"
#define IP "192.168.60.66"
#define PORT 7777
int main(int argc, const char *argv[])
{
//1、创建基于UDP的套接字
int oldfd = socket(AF_INET,SOCK_DGRAM,0);
if(oldfd==-1)
{
perror("socket");
return -1;
}
//2、设置允许加入组播组
struct ip_mreqn recv = {
.imr_multiaddr.s_addr = inet_addr(ZIP),//组播组IP
.imr_address.s_addr = inet_addr(IP),//本机IP
.imr_ifindex = 2 //ens33网卡索引号
};
if(setsockopt(oldfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&recv,sizeof(recv))==-1)
{
perror("setsockopt");
return -1;
}
//3、绑定
struct sockaddr_in recv2 = {
.sin_family = AF_INET,
.sin_port = htons(PORT),
.sin_addr.s_addr = inet_addr(ZIP)//绑定组播组IP
};
if(bind(oldfd,(struct sockaddr *)&recv2,sizeof(recv2))==-1)
{
perror("bind");
return -1;
}
//4、接收信息
struct sockaddr_in send;
socklen_t send_len = sizeof(send);
char buff[1024];
while(1)
{
recvfrom(oldfd,buff,sizeof(buff),0,(struct sockaddr *)&send,&send_len);
//recvfrom(oldfd,buff,sizeof(buff),0,NULL,NULL);不接收发送方的信息
printf("接收到%s发送来的信息:%s\n",inet_ntoa(send.sin_addr),buff);
if(strcmp(buff,"quit")==0)
{
printf("接收端也退出\n");
break;
}
}
close(oldfd);
return 0;
}