1. 概述
Unix域协议可以看做是IPC中的一种。它提供两类套接字,字节流套接字(类似TCP),数据报套接字(类似UDP。应用较少)。
使用它有以下三大优势:
- 比TCP套接字快出一倍多。
- 可以在不同进程之间传递文件描述符。
- 把用户凭据(用户ID、组ID)提供给服务器,从而进行安全检查。
Unix域中用于标识服务器和客户机的协议地址是普通文件的路径名。
2. 一个Unix域字节流C/S程序
2.1 创建Socket
int socket(int domain, int type, int protocol);
domain这里填AF_UNIX或AF_LOCAL用来标识是本地通信
type为SOCK_STREAM,字节流
2.2 删除路径并绑定
如果不unlink这个路径名,bind会失败。并这个路径一般是绝对路径,相对路径对C/S两端有同路径的限制。
这个文件的默认访问权限应该是0777,可按照当前的umask进行修正。比如umask是0022,那么创建文件的权限就是,0777-0022 = 0755。
套接字地址如下,
struct sockaddr_un {
sa_family_t sun_family; /*AF_LOCAL或AF_UNIX */
char sun_path[UNIX_PATH_MAX]; /* 路径名 UNIX_PATH_MAX是104*/
};
2.3 connect
客户端connect时必须和server端bind的文件路径是一致的。不同于TCP,这里connect调用发现监听套接字的队列满了的时候,立马就会返回一个ECONNECTREFUSED错误,TCP则会数次发送SYN进行重试。
3. 代码实例
server.c
#define PATH "/tmp/socket_file"
int main() {
// 1. 创建socket
int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("socket error");
return -2;
}
// 2. 删除连接,否则不能绑定
unlink(PATH);
struct sockaddr_un serv;
bzero(&serv, sizeof(serv));
serv.sun_family = AF_UNIX;
strncpy(serv.sun_path, PATH, sizeof(serv.sun_path) - 1);
// 3. 绑定
int ret = bind(sockfd, (struct sockaddr*)&serv, sizeof(serv));
if (ret < 0) {
perror("bind error");
return -3;
}
// 4. 监听
listen(sockfd, 5);
// 5. accept
int connfd = accept(sockfd, NULL, NULL);
if (connfd < 0) {
perror("accept error");
return -4;
}
char buff[1024];
while (1) {
ret = recv(connfd, buff, sizeof(buff), 0);
if (ret < 0) {
perror("recv error");
return -5;
}
write(1, buff, ret);
if (strncmp(buff, "quit", 4) == 0) {
break;
}
}
close(connfd);
close(sockfd);
return 0;
}
client.cpp
#define PATH "/tmp/socket_file"
int main(int argc, char *argv[]) {
int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("socket error");
return -1;
}
struct sockaddr_un serv;
bzero(&serv, sizeof(serv));
serv.sun_family = AF_UNIX;
strcpy(serv.sun_path, PATH);
int ret = connect(sockfd, (struct sockaddr *)&serv, sizeof(serv));
if (ret < 0) {
perror("connect error");
return -2;
}
char buff[1024];
while (1) {
ret = read(0, buff, sizeof(buff));
if (ret < 0) {
perror("read error");
}
ret = send(sockfd, buff, ret, 0);
if (ret < 0) {
perror("send error");
return -4;
}
if (strncmp(buff, "quit", 4) == 0) {
break;
}
}
close(sockfd);
return 0;
}