Unix域编程作用
Unix域编程用于同一台主机内部的进程之间的客户端/服务端通信,使用和网络socket编程相同的API。Unix域编程既可以使用SOCK_STREAM
类型的socket,也可以使用SOCK_DGRAM
类型的socket,二者使用的接口稍微不同。Unix域编程的优势包括:
- 效率高,比使用TCPsocket编程效率高一倍。
- 可以实现在同一主机的不同进程之间传递描述符。
- 可以实现把客户的凭证提供给服务器,提供额外的安全措施。
Unix域编程流程
针对SOCK_STREAM类型socket,编程流程如下所示,流程和TCP编程一致。
服务端:socket—>bind—>listen—>accept—>send/recv—>closesocket
客户端:socket—>bind(可选)—>connect—>send/recv---->closesocket
针对SOCK_DGRAM类型的socket,编程流程如下所示,和UDP流程不同的地方在于,UDP客户端可以不调用bind,内核会自动选择客户端地址,同时服务端通过recvfrom客户获取到这个地址,实现后续的双方通信。但是Unix域编程中,内核不会自动分配客户端地址,于是服务端就无法获取客户端地址,影响后续的通信,但是SOCK_STREAM类型的Unix域编程没有这个问题,虽然调用bind和connect不会自动分配地址,但是服务端通过accept返回的文件描述符,可以用来和客户端的通信:
服务端:socket()创建套接字 --> bind()绑定监听地址 --> recvfrom()等待接收客户端数据 --> sendto()发送应答数据给客户端
客户端:socket()创建套接字 -->bind()绑定客户端地址–> sendto()向服务端发送数据 --> recvfrom()接收服务端应答数据 --> close()关闭套接字
Unix域编程的地址格式
Unix域编程和网络域编程使用同一套接口,也需要使用地址结构,它的地址结构如下所示:
#include <sys/un.h>
struct sockaddr_un {
sa_family_t sun_family;//AF_LOCAL
char sun_path[104];//绝对路径,并且文件不存在。可以使用空字符串,含义类似INADDR_ANY。
}
Unix编程注意事项
sockaddr_un.sun_path
中的文件必须不存在,否则调用bind
的时候会报错,因此再调用bind之前最好先使用unlink
删除这个路径名。bind
成功后,使用ls -l
可以看到文件的类型是s(socket)类型。
[root@localhost ~]# ll /tmp/unix.str
srwxr-xr-x 1 root root 0 Mar 19 18:40 /tmp/unix.str
- 创建socket的时候地址族选择AF_LCOAL,类型选择SOCK_STREAM或SOCK_DGRAM,协议用0.
- SOCK_DGRAM类型的socket,客户端必须调用bind,SOCK_STREAM则没有这个要求。
Unix编程简单示例
附上unp书上的两个实例:
客户端实例
#include "unp.h"
void str_cli(FILE *fp, int sockfd);
int
main(int argc, char **argv)
{
int sockfd, n;
struct sockaddr_un servaddr, cliaddr;
char buff[128];
sockfd = socket(AF_LOCAL, SOCK_STREAM, 0);
bzero(&cliaddr, 0);
cliaddr.sun_family = AF_LOCAL;
strncpy(cliaddr.sun_path, "/tmp/cli_str", sizeof(cliaddr));
bzero(&servaddr, sizeof(servaddr));
servaddr.sun_family = AF_LOCAL;
strncpy(servaddr.sun_path, UNIXSTR_PATH, sizeof(servaddr.sun_path) - 1);
bind(sockfd, (struct sockaddr *)&cliaddr, sizeof(cliaddr));
connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
if ((n = recv(sockfd, buff, sizeof(buff), 0)) > 0) {
printf("recv : %s", buff);
}
exit(0);
}
服务端实例
#include "unp.h"
int
main(int argc, char **argv)
{
int listenfd, connfd;
pid_t childpid;
socklen_t clilen;
struct sockaddr_un cliaddr, servaddr;
char buff[128];
listenfd = socket(AF_LOCAL, SOCK_STREAM, 0);
unlink(UNIXSTR_PATH);
bzero(&servaddr, sizeof(servaddr));
servaddr.sun_family = AF_LOCAL;
strncpy(servaddr.sun_path, UNIXSTR_PATH, sizeof(servaddr.sun_path) - 1);
printf("server bind to %s\n", UNIXSTR_PATH);
bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
listen(listenfd, LISTENQ);
for ( ; ; ) {
clilen = sizeof(cliaddr);
if ((connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &clilen) ) < 0) {
perror("accept failed");
}
printf("recv client path %s\n", cliaddr.sun_path);
if ((childpid = fork()) == 0) {
close(listenfd);
strncpy(buff, "hello", sizeof(buff));
send(connfd, buff, sizeof(buff), 0);
exit(0);
}
close(connfd);
}
}