UNIX域套接字用于在同一台计算机上运行的进程之间的通信。
UNIX域套接字提供流和数据报两种接口。UNIX域数据报服务是可靠的,既不会丢失报文也不会传递出错。
使用socketpair函数来创建一对无命名的、相互连接的UNIX域套接字。
#include
int socketpair(int domain,int type,int protocol,int sockfd[2]);
//若成功,返回0;若出错,返回-1
一对相互连接的UNIX域套接字可以起到全双工管道的作用:两端对读和写开放。将其称为fd管道。
虽然socketpair函数能创建一对相互连接的套接字,但是每一个套接字都没有名字,无关进程不能使用它们。
UNIX域套接字的地址由sockaddr_un结构表示。在linux 3.2.0中,sockaddr_un结构在头文件
struct sockaddr_un{
sa_family_t sun_family; /*AF_UNIX*/
char sun_path[108]; /*pathname*/
};
sockaddr_un结构的sun_path成员包含一个路径名。当我们将一个地址绑定到一个UNIX域套接字时,系统会用该路径名创建一个S_ISSOCK类型的文件。该文件仅用于向客户进程告示套接字名字。该文件无法打开,也不能由应用程序用于通信。
如果我们试图绑定同一地址时,该文件已经存在,那么bing请求会失败。当关闭套接字时,并不自动删除该文件,所以必须确保在应用程序推出前,对该文件执行解除链接操作。
确定socklen_t的方法:
size=offsetof(struct sockaddr_un,sun_path)+strlen(un.sun_path
服务器进程可以使用标准bind、listen和accept函数,为客户进程安排一个唯一UNIX域连接。客户进程使用connect与服务器进程联系。在服务器进程接受了connect请求后,在服务器进程和客户进程之间就存在了唯一连接。
我们将开发3个函数,使用这些函数可以在运行于同一台计算机上点的两个无关进程之间穿点唯一连接:
#include "apue.h"
int serv_listen(const char *name);
//若成功,返回要监听的文件描述符;若出错,返回负值
int serv_accept(int listenfd,uid_t *uidptr);
//若成功,返回新文件描述符;若出错,返回负值
int cli_conn(const char *name);
//若成功,返回文件描述符;若出错,返回负值
服务器进程可以调用serv_listen函数声明它要在一个众所周知的名字(文件系统中的某个路径名)上监听客户进程的连接请求。serv_listen函数的返回值是用于接受客户进程连接请求的服务器UNIX域套接字。
客户进程调用cli_conn函数连接至服务器进程。客户进程指定的name参数必须与服务器进程调用serv_listen函数时所用的名字相同。
在两个进程之间传送打开文件描述符的技术是非常有用的。因此可以对客户进程-服务器进程应用进行不同的设计。它使一个进程(通常是服务器进程)能够处理打开一个文件所要做的一切操作以及向调用进程送回一个描述符,该描述符可被用于以后的所有I/O函数。涉及打开文件或设备的所有细节对客户进程而言都是透明的。
当一个进程向另一个进程传送一个打开文件描述符时,我们想让发送进程和接收进程共享同一文件表项:
在技术上,我们是将指向一个打开文件表项的指针从一个进程发送到另外一个进程。该指针被分配存放在接收进程的第一个可用描述符中。两个进程共享同一个打开文件表,这与fork之后的父进程和子进程共享打开文件表的情况完全相同。
#include "apue.h
int send_fd(int fd,int fd_to_send);
int send_err(int fd,int status,const char *errmsg);
//若成功,返回0;若出错,返回-1
int recv_fd(int fd,ssize_t (*userfunc)(int,const void*,size_t));
//若成功,返回文件描述符;若出错,返回负值
send_fd使用fd代表的UNIX域套接字发送描述符fd_to_send。send_err使用fd发送errmsg以及后随的status字节。status的值应在-1~-255.
为了用UNIX域套接字交换文件描述符,调用sendmsg和recvmsg函数。这两个函数的参数中都有一个指向maghdr结构的指针,该结构包含了所有关于要发送或要接收的消息的信息:
struct msghdr{
void *msg_name;
socklen_t msg_namelen;
struct iovec *msg_iov;
int msg_iovlen;
void *msg_control;
socklen_t msg_controllen;
int msg_flags;
};
前两个元素通常用于在网络连接上发送数据报。msg_flags字段包含了描述接收到的消息的标志。
两个元素处理控制信息的传送和接收。msg_control字段指向cmsghdr(控制信息头)结构,msg_controllen字段包含控制信息的字节数。
struct cmsghdr {
socklen_t cmsg_len;
int cmsg_level;
int cmsg_type;
/*followed by the actual control message data*/
};
为了发送文件描述符,将cmsg_len设置为cmsghdr结构的长度加一个整型的长度(描述符的长度),cmg_level字段设置为SOL_SOCKET,cmsg_type字段设置为SCM_RIGHTS,用以表明在传送访问权。访问权仅能通过UNIX域套接字传送。描述符紧随cmsg_type字段之后存储,用CMSG_DATA宏获得该整型量的指针。
#include
unsigned char *CMSG_DATA(struct cmsghdr *cp);
//返回一个指针,指向与cmsghdr结构相关联的数据
struct cmsghdr *CMSG_FIRSTHDR(struct msghdr *mp);
//返回一个指针,指向与msghdr结构相关联的第一个cmsghdr结构;若无这样的结构,返回NULL
struct cmsghdr *CMSG_NXTHDR(sturct msghdr *mp,
struct cmsghdr *cp);
//返回一个指针,指向与msghdr结构相关联的下一个cmsghdr结构,该msghdr结构给出了当前的cmsghdr结构;若当前cmsghdr结构已是最后一个,返回NULL
unsigned int CMSG_LEN(unsigned int nbytes);
//返回为nbytes长的数据对象分配的长度。
回忆serv_accept函数确定调用者身份的步骤。如果内核能够把调用者的证书在调用accept之后返回给调用处会更好。
在linux中,将证书作为ucred结构传送:
struct ucred {
pid_t pid;
uid_t uid;
gid_t gid;
};
注:需要在传输前初始化这个结构。
#include
int getopt(int argc,char *const argv[],const char *options);
extern int optind,opterr,optopt;
extern char *optarg;
若所有选项被处理完,返回-1;否则,返回下一个选项字符。
参数argc和argv与传入main函数的一样。options参数是一个包含该命令支持的选项字符的字符串。如果一个选项字符后面接了一个冒号,则表示该选项需要参数;否则,该选项不需要额外参数。
函数getopt一般用在循环体内,循环直到getopt返回-1时退出。每次迭代中,getopt会返回下一个选项。
当遇到无效的选项时,getopt返回一个问题标记而不是这个字符。如果选项缺少参数,getopt也会返回一个问题标记,但如果选项字符串的第一个字符是冒号,getopt会直接返回冒号。而特殊的“–”格式则会导致getopt**停止处理选项**并返回-1。这允许用户传递以“-”开头但不是选项的参数。