您的位置:首页 > 大数据 > 人工智能

UNIX域套接字(unix domain)

2015-09-06 20:15 316 查看
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\n");
exit(-1);
}
size = sizeof(struct sockaddr_un);
if (bind(fd, (struct sockaddr *)&un, size) < 0) {
printf("bind failed:[%s]\n",strerror(errno));
exit(-1);
}
printf("UNIX domain socket bound\n");
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; //增加\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);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: