UNIX域套接字(unix domain)

UNIX域套接字用于在同一台机器上运行的进程之间的通信。
UNIX域套接字提供流和数据报两种接口。
说明:UNIX域套接字比因特网套接字效率更高。它仅赋值数据;不进行协议处理,如添加或删除网络报头、计算校验和、产生顺序号、发送确认报文等等。 
 
创建一对非命名的、相互连接的UNIX域套接字。
socketpair
 
1.命名UNIX域套接字
1)套接字地址格式,在linux下如下所示
struct sockaddr_un {
 sa_family_t sun_family;
 char sun_path[108];
}
绑定该地址:
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <stdio.h>
#include <sys/un.h>
#include <errno.h>
int main(void)
{
    int fd,size;
    struct sockaddr_un un;
    un.sun_family = AF_UNIX; //unix域
    strcpy(un.sun_path, "foo.socket");
    if ((fd=socket(AF_UNIX, SOCK_STREAM, 0))<0) {
        printf("socket failed
");
        exit(-1);
    }
    size = sizeof(struct sockaddr_un);
    if (bind(fd, (struct sockaddr *)&un, size) < 0) {
        printf("bind failed:[%s]
",strerror(errno));
        exit(-1);
    }
    printf("UNIX domain socket bound
");
    exit(0);
}

# ls -l

srwxr-xr-x   1 dev_old_run swdev     0 Sep  7 13:49 foo.socket
第一位代表文件类型,它是一个socket文件
2.唯一连接
1)serv_listen函数
使用bind、listen和accept
2)serv_accept函数
accept
验证客户进程的身份是该套接字的所有者:验证路径名为一个套接字、权限仅允许用户-读、用户-写以及用户-执行、与套接字相关联的3个时间不比当前时间早30秒。
验证身份这块,我这里理解的是服务器对该路径的判断,只要判断出权限,基本上就可以确定客户端的身份了。
 
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include <time.h>
#include <stddef.h>

#define QLEN 10
#define STALE 30

int main(void)
{
}

//创建服务端,成功返回fd,错误返回值<0
int serv_listen(const char *name)
{
    int fd,err,rval, len;
    struct sockaddr_un un;
    
    if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
        return -1;
    unlink(name);  //存在文件,先解除连接
    
    //填充socket地址结构
    memset(&un, 0, sizeof(un));
    un.sun_family = AF_UNIX;
    strcpy(un.sun_path, name);
    
    //绑定地址到描述符
    if (bind(fd, (struct sockaddr *)&un, len) < 0) {
        rval = -2;
        goto errout;
    }
    
    if(listen(fd, QLEN) < 0) {
        rval = -3;
        goto errout;
    }
    return(fd);
    
    errout:
        err = errno;
        close(fd);
        errno = err;
        return(rval);
}

//等待客户连接,并接受它
//同时验证客户的身份
int serv_accept(int listenfd, uid_t *uidptr)
{
    int clifd, rval, err;
    socklen_t len;
    struct sockaddr_un un;
    struct stat statbuf;
    time_t staletime;
    
    len = sizeof(un);
    if ((clifd = accept(listenfd, (struct sockaddr *)&un, &len)) < 0) 
        return -1;
    
    //确定客户进程的身份是该套接字的所有者
    len -= offsetof(struct sockaddr_un, sun_path); //路径长
    un.sun_path[len]=0; //增加结束符
    
    if (stat(un.sun_path, &statbuf) < 0) {
        rval = -2;
        goto errout;
    }
    
    // 文件类型检查
    if (S_ISSOCK(statbuf.st_mode)==0) {
        rval = -3;
        goto errout;
    }
    
    // 文件权限检查
    if((statbuf.st_mode & (S_IRWXG | S_IRWXO)) ||
        statbuf.st_mode & S_IRWXU != S_IRWXU) {
        rval = -4;
        goto errout;
    }
    
    staletime = time(NULL) - STALE;
    if (statbuf.st_atime < staletime ||
        statbuf.st_ctime < staletime ||
        statbuf.st_mtime < staletime) {
        rval = -5;
        goto errout;
    }
    
    if (uidptr != NULL)
        *uidptr = statbuf.st_uid; //返回uid
    unlink(un.sun_path);
    return(clifd);
    
    errout:
        err = errno;
        close(clifd);
        errno = err;
        return rval;
}
3)cli_conn函数
客户端会绑定一个路径名,设置其权限。这样在serv_accept中,服务器可以检验这些权限等来验证客户进程的身份。
接着填充服务器的sockaddr_un结构,调用connect连接到服务器。
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <errno.h>
#include <sys/stat.h>
#include <string.h>
#include <sys/un.h>
#include <stddef.h>

#define CLI_PATH "/var/tmp/"
#define CLI_PERM S_IRWXU

int main(void)
{
    exit(0);
}

int cli_conn(const char *name)
{
    int fd, len, err, rval;
    struct sockaddr_un un;
    
    if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
        return -1;
    
    //填充客户端地址
    memset(&un, 0, sizeof(un));
    un.sun_family = AF_UNIX;
    sprintf(un.sun_path, "%s%05d", CLI_PATH, getpid()); 
    len = offsetof(struct sockaddr_un, sun_path) + strlen(un.sun_path);
    unlink(un.sun_path);
    //绑定到套接字
    if (bind(fd, (struct sockaddr *)&un, len) < 0) {
        rval = -2;
        goto errout;
    }
    
    if (chmod(un.sun_path, CLI_PERM) < 0) {
        rval = -3;
        goto errout;
    }
    
    //填充服务端地址
    memset(&un, 0, sizeof(un));
    un.sun_family = AF_UNIX;
    strcpy(un.sun_path, name); 
    len = offsetof(struct sockaddr_un, sun_path) + strlen(un.sun_path);
    if (connect(fd, (struct sockaddr *)&un, len) < 0) {
        rval = -4;
        goto errout;
    }
    return(fd);
    
errout:
    err = errno;
    close(fd);
    errno = err;
    return(rval);
}
原文地址:https://www.cnblogs.com/yanxin880526/p/4787046.html