一、网络属性

1> 对于套接字而言,在不同的层中,可以设置不同的属性,如端口号快速重用、超时时间、设置广播、加入多播组等等

2> 关于网络属性,有两个函数,分别是 setsockopt、getsockopt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#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
tcp传输层:IPPROTO_TCP
udp传输层: IPPROTO_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
tcp传输层:IPPROTO_TCP
udp传输层: IPPROTO_UDP
网络层:IPPROTO_IP
参数3:指定层内的属性,见下表
参数4:设置该属性的容器起始地址
参数5:参数4的大小
返回值:成功返回0,失败返回-1并置位错误码

img

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#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 resue = -1;
int resue_size = sizeof(resue);
if(getsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &resue, &resue_size)==-1)
{
perror("getsockopt error");
return -1;
}
printf("resue = %d\n", resue); //0

//设置端口号快速重用
resue = 1;
if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &resue, sizeof(resue)) == -1)
{
perror("setsockopt error");
return -1;
}
printf("端口号快速重用设置成功\n");

//再次获取网络属性
resue = -1;
if(getsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &resue, &resue_size)==-1)
{
perror("getsockopt error");
return -1;
}
printf("resue = %d\n", resue); //


return 0;
}

img

二、多点通信

2.1 单播

1> 单播发生在主机之间一对一的通信模式,交换机或者路由器只对数据进行转发,不做复制

2> 每次只有两个实体之间进行相互通信,发送端和接收端都是唯一确定的

2.2 广播

1> 主机之间的一对多的通信模式,网络对其中的每一台主句发出的信息都进行复制并转发

2> 所有主机都可以收到广播消息(无论你是否愿意接收),所以,广播是基于UDP通信模式

3> 广播地址:网络号 + 255

例如:主机地址为192.168.125.171 —> 192.168.125.255

4> 广播消息是不能穿过路由器的,也就是说广播消息禁止在外网上进行传播,所以广播只能完成局域网内的多点通信

2.3 广播的发送端模型 ----> 类似于UDP的客户端

1> socket 创建套接字

2> setsockopt 设置网络属性,允许广播

3> bind 非必须绑定

4> 填充接收端地址信息结构体

ip:填广播地址(192.168.125.255)

port:与接收端保持一致

5> sendto 发送消息

6> close 关闭套接字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#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 cin;
cin.sin_family = AF_INET;
cin.sin_port = htons(6789);
cin.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, sizeof(sbuf), 0, (struct sockaddr*)&cin, sizeof(cin));
printf("发送成功\n");
}


//6、关闭套接字
close(sfd);

return 0;
}

2.4 广播的接收端模型 ----> 类似于UDP的服务器端

1> socket 创建套接字

2> 填充地址信息结构体

ip:广播地址

port:与发送端保持一致

3> bind 绑定端口号与ip地址

4> recvfrom 接收消息

5> close 关闭套接字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#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、填充地址信息结构体
struct sockaddr_in rin;
rin.sin_family = AF_INET;
rin.sin_port = htons(6789); //端口号
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));

//接收消息
recvfrom(rfd, rbuf, sizeof(rbuf), 0, 0, 0);

printf("收到消息为:%s\n", rbuf);
}

//5、关闭套接字
close(rfd);

return 0;
}

2.5 组播

1> 组播也是实现主机之间一对多的通信模型,跟广播不同的是,组播发送的消息,只有加入多播组的成员才能收到,没有加入的就无法收到,不会占用柜台对的网络带宽

2> 组播也是使用UDP实现

3> 组播地址:就是D类网络,224.0.0.0 – 239.255.255.255

2.6 组播的发送端模型 —>类似于UDP的客户端

1> socket 创建套接字

2> bind 非必须绑定

3> 填充接收端地址信息结构体

ip:组播地址,与接收端保持一致

port:与接收端保持一致

4> sendto 发送组播消息

5> close 关闭套接字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#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、绑定 非必须

//3、填充地址信息结构体
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(9999);
sin.sin_addr.s_addr = inet_addr("224.1.2.3");

//4、发送消息
char sbuf[128] = "";
while(1)
{
printf("请输入>>>");
fgets(sbuf, sizeof(sbuf), stdin);
sbuf[strlen(sbuf)-1] = 0;

//将消息发送出去
sendto(sfd, sbuf, sizeof(sbuf), 0, (struct sockaddr*)&sin, sizeof(sin));
printf("发送成功\n");
}

//5、关闭套接字
close(sfd);
return 0;
}

2.7 组播的接收端模型 —> 类似于UDP的服务器

1> socket 创建套接字

2> setsockopt 设置网络属性

设置层级:IPPROTO_IP

设置属性:IP_ADD_MEMBERSHIP

属性类型:

struct ip_mreqn {

struct in_addr imr_multiaddr; /* IP multicast group address / //组播ip地址

struct in_addr imr_address; / IP address of local interface / //本地IP地址

int imr_ifindex; / interface index */ 网络索引 0表示使用默认网络索引

​ ip ad:查看自己的网卡索引号 ens33 —> 2

};

3> 填充地址信息结构体

ip:组播IP,与发送端保持一致

port :与发送端保持一致

4> bind 必须绑定

5> recvfrom 接收消息

6> close 关闭套接字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#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、设置网络属性
struct ip_mreqn im;
im.imr_multiaddr.s_addr = inet_addr("224.1.2.3"); //组播地址
im.imr_address.s_addr = inet_addr("192.168.122.118"); //本机ip
im.imr_ifindex = 2; //网络索引

if(setsockopt(rfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &im, sizeof(im)) == -1)
{
perror("setsockopt error");
return -1;
}
printf("加入多播组成功\n");

//3、填充地址信息结构体
struct sockaddr_in rin;
rin.sin_family = AF_INET;
rin.sin_port = htons(9999);
rin.sin_addr.s_addr = inet_addr("224.1.2.3");

//4、绑定
if(bind(rfd, (struct sockaddr*)&rin, sizeof(rin)) == -1)
{
perror("bind error");
return -1;
}

//5、接收消息
char rbuf[128] = "";
while(1)
{
bzero(rbuf, sizeof(rbuf));
recvfrom(rfd, rbuf, sizeof(rbuf), 0, 0, 0);
printf("收到消息为:%s\n", rbuf);
}

//6、关闭套接字
close(rfd);

return 0;
}

本章完