所谓基于流的管道实际上就是一种全双工管道,它必须在基于流的系统上才能实现,linux 默认对它是不支持的,而同样的逻辑,我们通常可以用基于 UNIX domain 的 socket 来实现,所以这里对它只作简单介绍。
关于流机制,我在 Unix环境高级编程学习笔记(九) 高级IO中曾经介绍过,知道可以在流首处加入处理模块,对于基于流的管道而言,管道的两端都是流首,所以可以在两端都加入处理模块,但同时,我们也只能在处理模块加入的一端删除它。
有时候,为了达到在不相关的进程间通信的目的,我们可以将这种管道的一端和某个实际的文件关联起来,也就相当于给了它一个名字,使用 fattach 函数:
- int fattach(int filedes, const char *path);
调用进程必须拥有该文件,且对它具有写权限,或是调用进程具有超级用户的权限。而一旦流管道的一端和某个文件关联上后,该文件就不能再被访问了,任何对该文件执行的打开操作,都是获得管道的访问权,而不再是那个文件本身。不过,对于管道关联之前就已经打开了文件的进程,仍然可以继续访问该文件,而不必受管道的影响。
使用 fdetach 函数可以解除关联:
- int fdetach(const char *path);
在该函数被调用以后,已经获得管道访问权的用户将继续拥有该管道的访问权,而之后打开该文件的进程获得的是对文件本身的访问权。
UNIX 域的 SOCKET
在 Unix环境高级编程学习笔记(十一) 网络IPC:套接字中,我介绍了 socket 使用,关于 domain,一共可以有4种情况,今天,我们来看一下用于同一机器下不同进程间通信的 UNIX
域。UNIX 域同时支付流和数据报接口,不同于英特网域,这两种接口都是可靠的。
我们可以像使用普通的 socket 那样来使用它,在 UNIX 域的限定下,其地址格式由 sockaddr_un 数据结构来呈现。在Linux 2.4.22 以及 Solaris 9 上,sockaddr_un 的定义如下:
- struct sockaddr_un {
- sa_family_t sun_family;/* AF_UNIX */
- char sun_path[108];/* pathname */
- };
sun_path 成员指定了一个文件名,当将一个 UNIX domain socket 和该地址绑定在一起之后,系统将为我们创建一个 S_IFSOCK 类型的该文件。当该文件已经存在时,bind 函数将失败。当我们关闭该 socket 之后,文件不会自动被删除,需要我们手动 unlink 它。
需要注意以几点:
1. 在connect调用中指定的路径名必须是一个当前绑定在某个打开的Unix域套接口上的路径名。如果对于某个Unix域套接口的connect调用发现这个监听套接口的队列已满,调用就立即返回一个ECONNREFUSED错误。这一点不同于TCP,如果TCP监听套接口队列已满,TCP监听端就忽略新到达的SYN,而TCP连接发送端将数次发送SYN进行重试。
2. 在一个未绑定的Unix域套接口上发送数据报不会自动给这个套接口捆绑一个路径名,这一点与UDP套接口不同。所以在客户端,我们也必须显示地bind一个路径名到我们的套接口。
来看一个实际的例子。
服务端:
- /*
- *author: justaipanda
- *create time:2012/09/05 21:51:27
- */
- #include <stdio.h>
- #include <stdlib.h>
- #include <sys/socket.h>
- #include <sys/un.h>
- #define FILE_NAME "1.txt"
- #define BUF_SIZE 1024
- int serv_listen(const char* name) {
- int fd;
- if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
- return -1;
- unlink(name);
- struct sockaddr_un un;
- memset(&un, 0, sizeof(un));
- un.sun_family = AF_UNIX;
- strcpy(un.sun_path, name);
- int len = (int)((void*)&(un.sun_path) - (void*)&un)
- + strlen(un.sun_path);
- if (bind(fd, (struct sockaddr*)&un, len) < 0) {
- close(fd);
- return -2;
- }
- if (listen(fd, 128) < 0) {
- close(fd);
- return -3;
- }
- return fd;
- }
- int serv_accept(int listenfd) {
- int fd;
- struct sockaddr_un un;
- int len = sizeof(un);
- if ((fd = accept(listenfd, (struct sockaddr*)&un, &len)) < 0)
- return -1;
- ((char*)(&un))[len] = '\0';
- unlink(un.sun_path);
- return fd;
- }
- int main() {
- int fd;
- if ((fd = serv_listen(FILE_NAME)) < 0) {
- printf("listen error!\n");
- exit(fd);
- }
- while(1) {
- int sclient = serv_accept(fd);
- if (sclient < 0) {
- printf("accept error!\n");
- exit(sclient);
- }
- char buffer[BUF_SIZE];
- ssize_t len = recv(sclient, buffer, BUF_SIZE, 0);
- if (len < 0) {
- printf("recieve error!\n");
- close(sclient);
- continue;
- }
- buffer[len] = '\0';
- printf("receive[%d]:%s\n", (int)len, buffer);
- if (len > 0) {
- if('q' == buffer[0]) {
- printf("server over!\n");
- exit(0);
- }
- char* buffer2 = "I'm a server!";
- len = send(sclient, buffer2, strlen(buffer2), 0);
- if (len < 0)
- printf("send error!\n");