一、域套接字相关概念

1> 只能在同一主机之间完成多个进程间的通信方式,是最原始的套接字通信模型

2> 由于不需要借助网络,所以在通信时,无需使用ip地址和端口号

3> 会在内核空间使用 套接字文件 进行通信

4> bcd-lsp 中的s,说的就是套接字文件类型

5> socket函数的说明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
       #include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>

int socket(int domain, int type, int protocol);
功能:创建一个用于通信的端点,并返回该通信对应的文件描述符,描述符使用最小未分配原则
参数1:通信域对应的协议族
AF_UNIX, AF_LOCAL Local communication(本地通信) unix(7) man 7 unix可以查看相信信息
AF_INET IPv4 Internet protocols(IPv4通信) ip(7) man 7 ip可以查看相信信息
AF_INET6 IPv6 Internet protocols(IPv6通信) ipv6(7) man 7 ipv6可以查看相信信息
参数2:指定通信语义,理解成传输方式
SOCK_STREAM 提供支持TCP通信

SOCK_DGRAM 提供支持UDP通信
SOCK_RAW 通过原始的套接字通信
参数3:通信协议,如果参数2指定了确定的通信方式,该参数填0即可
如果不确定通信方式,可用的参数有:
TCP:IPPROTO_TCP
UDP:IPPROTO_UDP
返回值:成功返回套接字文件描述符,失败返回-1并置位错误码

二、流式域套接字

1> 基于TCP通信原理,面向连接的通信方式

2> bind函数,只能绑定一个不存在的套接字文件,如果绑定的套接字文件存在,则bind函数报错:Address already in use

3> 对于客户端而言,如果不绑定一个套接字文件,系统不会给客户端绑定套接字文件

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
       #include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
功能:给指定的套接字文件描述符绑定IP地址和端口号
参数1:要绑定的套接字文件描述符
参数2:地址信息结构体,包含了通信域、IP地址、端口号
struct sockaddr {
sa_family_t sa_family;
char sa_data[14];
}
以上结构体是通用地址信息结构体,无论TCP还是UDP都可以使用,主要用于形参的强制类型转换,避免出现编译报错,但是具体的通信族有不同的地址信息结构体
对于IPv4通信,需要man 7 ip进行查看
struct sockaddr_in {
sa_family_t sin_family; /* address family: AF_INET */ 通信域
in_port_t sin_port; /* port in network byte order */ 端口号,网络字节序
struct in_addr sin_addr; /* internet address */ 网络地址,是一个结构体
};
struct in_addr {
uint32_t s_addr; /* address in network byte order */ IP地址,网络字节序
};

对于本地的通信,需要man 7 unix查看
struct sockaddr_un {
sa_family_t sun_family; /* AF_UNIX */ 标识域套接字
char sun_path[108]; /* pathname */ 套接字文件的名字
};
参数3:参数2的大小
返回值: 成功返回0,失败返回-1并置位错误码



3> 判断文件是否存在函数:access

1
2
3
4
5
6
7
8
9
10
11
       #include <unistd.h>

int access(const char *pathname, int mode);
功能:判断给定的文件是否具有给定的权限
参数1:要检测的文件,是一个文件路径
参数2:要检测的权限
R_OK:判断是否具有读权限
W_OK:判断是否具有写权限
X_OK:判断是否具有可执行权限
F_OK:判断是否存在
返回值:如果检查的权限存在,则返回0,否则返回-1并置位错误码

4> 删除文件系统中某个文件的函数:unlink

1
2
3
4
5
6
       #include <unistd.h>

int unlink(const char *path);
功能:删除指定的文件
参数:要删除的文件文件路径
返回值:成功返回0,失败返回-1并置位错误码

2.1 流式域套接字服务器端实现

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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#include<myhead.h>

int main(int argc, const char *argv[])
{
//1、创建套接字
int sfd = socket(AF_UNIX, SOCK_STREAM, 0);
//int sfd = socket(AF_UNIX, SOCK_RAW, IPPROTO_TCP);
if(sfd == -1)
{
perror("socket error");
return -1;
}


//由于域套接字的绑定函数,只能绑定一个不存在的套接字文件
//所以,在绑定之前需要判断当前文件是否存在
if(access("./unix", F_OK) == 0)
{
//表示文件存在,删除该文件
if(unlink("./unix")==-1)
{
perror("unlink error");
return -1;
}
}

//2、填充地址信息结构体
struct sockaddr_un sun;
sun.sun_family = AF_UNIX; //通信域
//sun.sun_path = ".unix"; //字符串赋值不能使用赋值运算符
strcpy(sun.sun_path, "./unix"); //绑定套接字文件

//3、绑定地址信息结构体
if(bind(sfd, (struct sockaddr*)&sun, sizeof(sun)) == -1)
{
perror("bind error");
return -1;
}
printf("bind success\n");

//4、监听
if(listen(sfd, 128) ==-1)
{
perror("listen error");
return -1;
}

//5、阻塞接收客户端链接请求
//定义容器接收客户端地址信息结构体
struct sockaddr_un cun;
socklen_t socklen = sizeof(cun);

int newfd = accept(sfd, (struct sockaddr*)&cun, &socklen); //表示不接收客户端地址信息
if(newfd == -1)
{
perror("accept error");
return -1;
}

//6、收发数据
char buf[128] = "";
while(1)
{
//清空数组
bzero(buf, sizeof(buf));

int res = recv(newfd, buf, sizeof(buf), 0); //读取消息
if(res == 0)
{
printf("客户端已经下线\n");
break;
}
printf("[%s]: %s\n", cun.sun_path ,buf);

}

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


return 0;
}

2.2 流式域套接字客户端实现

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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#include<myhead.h>

int main(int argc, const char *argv[])
{
//1、创建用于通信的套接字文件描述符
int cfd = -1;
cfd = socket(AF_UNIX, SOCK_STREAM, 0);
if(cfd == -1)
{
perror("socket error");
return -1;
}
printf("cfd = %d\n", cfd); //3

//由于域套接字的绑定函数,只能绑定一个不存在的套接字文件
//所以,在绑定之前需要判断当前文件是否存在
if(access("./linux", F_OK) == 0)
{
//表示文件存在,删除该文件
if(unlink("./linux")==-1)
{
perror("unlink error");
return -1;
}
}




//2、绑定(可选)如果客户端不绑定套接字文件,则系统不会自动绑定套接字文件
//2.1 填充地址信息结构体
struct sockaddr_un cun;
cun.sun_family = AF_UNIX;
strcpy(cun.sun_path,"./linux");

//2.2绑定
if(bind(cfd, (struct sockaddr*)&cun, sizeof(cun)) == -1)
{
perror("bind error");
return -1;
}
printf("bind success\n");


//3、连接服务器
//3.1 填充服务器地址信息结构体
struct sockaddr_un sun;
sun.sun_family = AF_UNIX;
strcpy(sun.sun_path, "./unix"); //要使用套接字文件进行通信

//3.2 连接
if(connect(cfd, (struct sockaddr*)&sun, sizeof(sun)) == -1)
{
perror("connect error");
return -1;
}
printf("connect success\n");


//4、收发数据
char buf[128] = "";
while(1)
{
//清空数组
bzero(buf, sizeof(buf));

printf("请输入>>>");
fgets(buf, sizeof(buf), stdin); //从终端输入数据
buf[strlen(buf)-1] = 0; //将换行改为'\0'

//发送给服务器
send(cfd, buf, sizeof(buf), 0);
printf("发送成功\n");
if(strcmp(buf, "quit") == 0)
{
break;
}


}

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

return 0;
}

三、报式域套接字

1> 基于UDP面向无连接的通信方式

2> 如果客户端没有绑定套接字,系统不会为其绑定套接字文件,当服务器想要给客户端发消息时:怎么办?

3.1 报式域套接字服务器端

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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#include<myhead.h>


int main(int argc, const char *argv[])
{
//1、创建套接字
int sfd = socket(AF_UNIX, SOCK_DGRAM, 0);
//SOCK_DGRAM:表示使用UDP报式套接字通信
if(sfd == -1)
{
perror("socket error");
return -1;
}
printf("sfd = %d\n", sfd);

//由于域套接字的绑定函数,只能绑定一个不存在的套接字文件
//所以,在绑定之前需要判断当前文件是否存在
if(access("./linux", F_OK) == 0)
{
//表示文件存在,删除该文件
if(unlink("./linux")==-1)
{
perror("unlink error");
return -1;
}
}


//2、绑定
//2.1 填充地址信息结构体
struct sockaddr_un sun;
sun.sun_family = AF_UNIX;
strcpy(sun.sun_path, "./linux");

//2.2 绑定工作
if(bind(sfd, (struct sockaddr*)&sun, sizeof(sun)) == -1)
{
perror("bind error");
return -1;
}

//3、数据收发
char buf[128] = "";
//定义变量存放客户端地址信息结构体
struct sockaddr_un cun;
socklen_t socklen = sizeof(cun);


while(1)
{
//清空数组
bzero(buf, sizeof(buf));

//从套接字文件中读取消息
recvfrom(sfd, buf, sizeof(buf), 0, (struct sockaddr*)&cun, &socklen);


printf("[%s]:%s\n", cun.sun_path, buf);

//将字符串连接后回发
strcat(buf, "*_*");
//if(write(sfd, buf, sizeof(buf)) == -1)
if(sendto(sfd, buf, sizeof(buf), 0, (struct sockaddr*)&cun, sizeof(cun)) == -1)
{
perror("write error");
return -1;
}
printf("发送成功\n");
}

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

return 0;
}

3.2 报式域套接字客户端

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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#include<myhead.h>


int main(int argc, const char *argv[])
{
//1、创建用于通信的套接字文件描述符
int cfd = socket(AF_UNIX, SOCK_DGRAM, 0);
if(cfd == -1)
{
perror("socket error");
return -1;
}

//由于域套接字的绑定函数,只能绑定一个不存在的套接字文件
//所以,在绑定之前需要判断当前文件是否存在
if(access("./unix", F_OK) == 0)
{
//表示文件存在,删除该文件
if(unlink("./unix")==-1)
{
perror("unlink error");
return -1;
}
}


//2、绑定
//2.1 填充地址信息结构体
struct sockaddr_un cun;
cun.sun_family = AF_UNIX;
strcpy(cun.sun_path, "./unix");

//2.2 绑定工作
if(bind(cfd, (struct sockaddr*)&cun, sizeof(cun)) == -1)
{
perror("bind error");
return -1;
}




//3、填充服务器的地址信息结构体
struct sockaddr_un sun;
sun.sun_family = AF_UNIX;
strcpy(sun.sun_path, "./linux");


//4、数据收发
char buf[128] = "";
while(1)
{
printf("请输入>>>");
fgets(buf, sizeof(buf), stdin);
buf[strlen(buf)-1] = '\0';

//发送给服务器
sendto(cfd, buf, sizeof(buf), 0, (struct sockaddr*)&sun, sizeof(sun));

if(strcmp(buf, "quit") == 0)
{
break;
}

//接收服务器发送来的消息
recvfrom(cfd, buf, sizeof(buf), 0, NULL, NULL);
printf("收到消息:%s\n", buf);


}

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


return 0;
}

本章完