一、UNIX域套接字
- UNIX域套接字作用在同一台计算机上运行的两个进程间的通信。
- UNIX域套接字比因特尔网络套接字效率要高。它仅复制数据,不执行协议处理,不需要添加或删除网络报文头,无需计算校验和,不要产生顺序号,无需发送确认报文等等。
- UNIX域套接字有两种类型的套接字:字节流套接字和数据报套接字,字节流套接字类似于TCP,数据报套接字类似于UDP
- UNIX域套接字连接的一对套接字可以起到全双工管道的作用,是全双工的通信方式。
![](https://i-blog.csdnimg.cn/blog_migrate/6797aeb183ec90563c81e5eaf3ed5889.png)
二、数据结构
2.1 unix域套接字的地址结构
struct sockaddr_un {
sa_family_t sun_family; /* AF_UNIX 或 AF_LOCAL */
char sun_path[108]; /* pathname */
}
- sun_path是一个路径名,此路径名的属性为0777,可以进行读写等操作
- 这个路径名应该是一个绝对路径名,而不是一个相对路径名。因为解析相对路径名依赖调用者当前工作目录,如果使用使用相对路径名就必须要确保服务器与客户端程序运行在相同目录下,否则将会出现异常
- 除了普通路径名外,还可以使用抽象路径名。好处是不用担心出现文件已存在情况,同时程序结束,也会自动删除这个抽象名。但是,在计算抽象路径名长度的时候要注意。
2.2 创建套接字
/*
功能:socket的创建
参数:
domain:AF_UNIX 或 AF_LOCAL
type:套接字类型:字节流套接字(SOCK_STREAM)与数据报套接字(SOCK_DGRAM)
protocol:如果第二参数不是SOCK_RAM原始套接字类型,一般设为0
返回值:成功返回文件描述符,失败返回 -1
*/
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain,int type,int protocol);
2.3 地址绑定
/*
功能:在UNIX域socket中bind函数不是用来绑定IP和端口,而是绑定一个有效路径名
参数:
sockfd:socket函数返回的文件描述符
addr:套接字的地址结构,sockaddr_un类型
addrlen:地址长度
返回值:成功放回 0,失败返回 -1
*/
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd,const struct sockaddr *addr,socklen_t addrlen);
2.4 其它API函数
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
int listen(int sockfd,int backlog)
/* 监听套接字 */
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
/* 接收客户端建立连接请求 */
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
/* 客户端向服务器端发送连接请求 */
ssize_t read(int fd, void *buf, size_t count);
/* 进行读操作 */
ssize_t write(int fd, const void *buf, size_t count);
/* 进行写操作 */
- 这些函数的使用与因特尔网络socket基本类似,注意套接字地址结构体不要弄错就行
三、编程示例
3.1 客户端、服务器流程
![](https://i-blog.csdnimg.cn/blog_migrate/cf1969e673dbd31942f8158f3b63c03d.png)
3.2 unix_socket_server
1 #include <stdio.h>
2 #include <string.h>
3 #include <unistd.h>
4 #include <errno.h>
5 #include <sys/un.h>
6 #include <sys/types.h>
7 #include <sys/socket.h>
8
9 #define STR "I'm server"
10
11 int main(int argc,char **argv)
12 {
13 int rv=-1;
14 int socket_fd=-1;
15 int client_fd=-1;
16
17 char buf[1024];
18 char SOCK_PATH[108];
19
20 socklen_t addrlen=sizeof(struct sockaddr_un);
21 struct sockaddr_un serverAddr;
22 struct sockaddr_un clientAddr;
23
24
25 if(argc != 2)
26 {
27 printf("please input file name!\n");
28 return -1;
29 }
30
//使用snprintf函数将输入的pathname保存在SOCK_PATH中,这里要注意第二个参数字节的长度需要计入/0
31 snprintf(SOCK_PATH,strlen(argv[1])+1,"%s",argv[1]);
32
//创建UNIX域字节流套接字
33 socket_fd=socket(AF_UNIX,SOCK_STREAM,0);
34 if(socket_fd < 0)
35 {
36 printf("socket failure:%s\n",strerror(errno));
37 return -2;
38 }
39
//初始化地址结构体 serverAddr
40 memset(&serverAddr,0,sizeof(struct sockaddr_un));
41 serverAddr.sun_family=AF_UNIX;
42 serverAddr.sun_path[0]=0;
43 snprintf(serverAddr.sun_path+1,strlen(SOCK_PATH)+1,"%s",SOCK_PATH);
44 printf("sun_path:%s\n",serverAddr.sun_path+1);
45
//绑定地址
46 rv=bind(socket_fd,(struct sockaddr *)&serverAddr,addrlen);
47 if(rv < 0)
48 {
49 printf("bind failure:%s\n",strerror(errno));
50 return -3;
51 }
//监听
52 listen(socket_fd,13);
53
//接收客户端的连接请求
54 client_fd=accept(socket_fd,(struct sockaddr *)&clientAddr,&addrlen);
55 if(client_fd < 0)
56 {
57 printf("accept failure:%s\n",strerror(errno));
58 return -4;
59 }
60 printf("accept client_fd[%d]\n",client_fd);
61
62 while(1)
63 {
64 memset(buf,0,sizeof(buf));
65 rv=read(client_fd,buf,sizeof(buf)); //调用read函数读取客户端写入缓存区中的内容
66 if(rv <= 0)
67 {
68 printf("read failure:%s\n",strerror(errno));
69 close(client_fd); //连接断开记得关闭文件描述符
70 break;
71 }
72
73 printf("read %d word:%s\n",rv,buf);
74 }
75 close(socket_fd); //在程序退出前将打开的文件描述符都关闭
76 return 0;
77 }
3.3 unix_socket_client
1 #include <stdio.h>
2 #include <string.h>
3 #include <unistd.h>
4 #include <sys/types.h>
5 #include <sys/socket.h>
6 #include <errno.h>
7 #include <sys/un.h>
8
9 #define STDIN 0
10
11 int main(int argc,char **argv)
12 {
13 int rv = -1;
14 int client_fd = -1;
15 char SOCK_PATH[108];
16 char buf[1024];
17
18 struct sockaddr_un clientAddr;
19
20 if(argc != 2)
21 {
22 printf("please input pathname!\n");
23 return -1;
24 }
25
26 snprintf(SOCK_PATH,strlen(argv[1])+1,"%s",argv[1]);
27
//创建UNIX域字节流套接字
28 client_fd=socket(AF_UNIX,SOCK_STREAM,0);
29 if(client_fd < 0)
30 {
31 printf("socket failure:%s\n",strerror(errno));
32 }
33
//初始化地址结构体 clientAddr
34 memset(&clientAddr,0,sizeof(struct sockaddr_un));
35 clientAddr.sun_family = AF_UNIX;
36 clientAddr.sun_path[0]=0;
37 snprintf(clientAddr.sun_path+1,strlen(SOCK_PATH)+1,"%s",SOCK_PATH);
38
//向客户端发送连接请求
39 if(connect(client_fd,(struct sockaddr *)&clientAddr,sizeof(struct sockaddr_un)) < 0)
40 {
41 printf("connect failure:%s\n",strerror(errno));
42 return -2;
43 }
44
45 while(1)
46 {
47 memset(buf,0,sizeof(buf));
48 rv=read(STDIN,buf,sizeof(buf)); //从标准输入中读取内容存储到buf中
49 if(rv <= 0)
50 {
51 printf("STDIN read failure:%s\n",strerror(errno));
52 close(client_fd);
53 return -3;
54 }
55
56 rv=write(client_fd,buf,strlen(buf)); //将buf内存储的内容写入缓存区供服务器端读取,注:第三个参数应为buf实际数据大小
57 if(rv <= 0)
58 {
59 printf("write failure:%s\n",strerror(errno));
60 close(client_fd);
61 return -3;
62 }
63
64 }
65 return 0;
66 }
四、小结
- UNIX域套接字的地址结构体为 sockaddr_un;
- 创建套接字的协议是AF_UNIX或AF_LOCAL;
- 字节流套接字(SOCK_STREAM)和数据报套接字(SOCK_DAEAM)
- 可通过多进程、多线程、多路复用实现多客户端的并发访问