Unix域协议并不是一个实际的协议族,而是在单个主机上执行客户/服务器通信的一种方法,它其实是IPC(InterProcess Communication)中的一种,顺便提一下,进程间通信可用如下方式:管道(半双工),FIFOS(命名管理),流管道(全双工),命令流管道,消息队列,信号量,共享存储,套接口,流,前几种通常限于同一台主机的各个进程间通信,后两种可以是不同主机上的各进程间通信。
Unix域提供两类套口:字节流套接口和数据报套接口。使用Unix域套接口的理由有三个:
1,在源自Berkeley的实现中,Unix域套接口往往比通信两端位于同一个主机的TCP套接口快出一倍。
2,Unix域套接口可用于同一个主机上的不同进程之间传递描述字。
3,Unix域套接口较新的实现把客户的凭证(用户ID和组ID)提供给服务器,从而能够提供额外的安全检查措施。
Unix域套接口定义在<sys/un.h>中
struct sockaddr_un {
sa_family_t sun_family;
char sun_path[104];
};
注意存放在sun_path数组中的路径名必须以空字符结尾。实现提供的SUN_LEN宏以一个指向sockaddr_un结构的指针为参数并返回该结构的长度,其中包括路径名中非空字节数。
我们可以通过如下方式他那一个Unix套接口,并往其上bind一个路径名:
sockfd = socket( AF_LOCAL, SOCK_STREAM, 0 );
unlink(argv[1]);
memset( &addr, 0, sizeof(addr) );
addr.sun_family = AF_LOCAL;
strncpy( addr.sun_path, argv[1], sizeof(addr.sun_path)-1 );
bind( sockfd, (struct sockaddr*)&addr, SUN_LEN(&addr) );
这里是使用函数的第一个参数argv[1]作为路径名,首先调用unlink函数,以防止文件系统中已存在该路径名,如果存在,bind将会失效。如果这不存在,unlink将返回一个错误,我们忽略这一错误。后面strncpy函数中,由于sun_path的最后一个字符必须是null,所以在复制的时候,我们减1,以防止最后一位被更改。bind的长度用SUN_LEN取得。
这里还有一点需要注意的是POSIX规定结果路径的文件访问权限应根据unmask的值修正,也即是它的值决定了创建的域的读写权限。
socketpair函数
#include <sys/socket.h>
intsocketpair( int family, int type, int protocol, int sockfd[2] );
此函数只用于Unix域套接口,它的family必须是AF_LOCAL,protocol参数必须为0,type可以是SOCK_STREAM,此时得到的结果称为流管道,它与调用pipe创建的普通管道类似,差别在于流管道是全双工的,当然也可以是SOCK_DGRAM。
POSIX关于Unix域套接口的一些要求
1,由bind创建的路径名缺省访问权限应为0777,并根据umask值进行修正。
2,与Unix域套接口关联的路径名应该是一个绝对路径名,而不是一个相对路径名。
3,在connect调用中指定的路径名必须是一个当前绑定在某个打开的Unix域套接口上的路径名。如果对于某个Unix域套接口的connect调用发现这个监听套接口的队列已满,调用就立即返回一个ECONNREFUSED错误。这一点不同于TCP,如果TCP监听套接口队列已满,TCP监听端就忽略新到达的SYN,而TCP连接发送端将数次发送SYN进行重试。
4,在一个未绑定的Unix域套接口上发送数据报不会自动给这个套接口捆绑一个路径名,这一点与UDP套接口不同。所以在客户端,我们也必须显示地bind一个路径名到我们的套接口。