UNIX域套接字用于在同一台机器上运行的进程之间的通信。虽然因特网域套接字可用于同一目的,但UNIX域套接字的效率更高UNIX域套接字仅仅复制数据;它们并不执行协议处理,不需要添加或删除网络报头,无需计算检验和,不要产生顺序号,无需发送确认报文。

UNIX域套接字提供流和数据报两种接口。UNIX域数据报服务是可靠的,既不会丢失消息也不会传递出错。UNIX域套接字是套接字和管道之间的混合物。为了创建一对非命名的、相互连接的UNIX域套接字,用户可以使用它们面向网络的域套接字接口,也可使用socketpair函数

#include <sys/socket.h>
int socketpair(int domain, int type, int protocol, int sockfd[2]);
返回值:若成功则返回0,出错则返回-1

虽然该接口具有足够的一般性,socketpair可用于任意域,但操作系统通常仅对UNIX域提供支持。

实例:使用UNIX域套接字的s_pipe函数

程序清单17-6 s_pipe函数的套接字版本(创建一对相连接的UNIX域流套接字)

#include "apue.h"
#include <sys/socket.h> /*
* Return a full-duplex "stream" pipe (a UNIX domain socket)
* with the two file descriptors returned in fd[0] and fd[1].
*/
int
s_pipe(int fd[2])
{
return(socketpair(AF_UNIX, SOCK_STREAM, 0, fd));
}

某些基于BSD的系统使用UNIX域套接字实现管道。但当调用pipe时,第一描述符的写端和第二描述符的读端都被关闭。为了得到全双工管道,我们必须直接调用socketpair。

1、命名UNIX域套接字

虽然socketpair函数创建相互连接的一对套接字,但是每一个套接字都没有名字。这意味着无关进程不能使用它们。

http://www.cnblogs.com/nufangrensheng/p/3565402.html,我们学习了如何将一个地址绑定一因特网域套接字。恰如因特网域套接字一样,我们也可以命名UNIX域套接字,并可将其用于告示服务。但是要注意的是,UNIX域套接字使用的地址格式不同于因特网域套接字。

套接字地址格式可能随实现而变。UNIX域套接字的地址由sockaddr_un结构表示。在Linux 2.4.22和Solaris 9中,sockaddr_un结构按下列形式定义在头文件<sys/un.h>中。

struct sockaddr_un {
sa_family sun_family; /* AF_UNIX */
char sun_path[108]; /* pathname */
};

sockaddr_un结构的sun_path成员包含一路径名。当我们将以地址绑定至UNIX域套接字时,系统用该路径名创建一类型为S_IFSOCK的文件。

该文件仅用于向客户进程告知套接字名字。该文件不能打开,也不能由应用程序用于通信。

如果当我们试图绑定地址时,该文件已经存在,那么bind请求失败。当关闭套接字时,并不自动删除该文件,所以我们必须确保在应用程序终止前,对该文件执行解除链接操作。

实例

程序清单17-7 将一个地址绑定一UNIX域套接字

#include "apue.h"
#include <sys/socket.h>
#include <sys/un.h> int
main(void)
{
int fd, size;
struct sockaddr_un un; un.sun_family = AF_UNIX;
strcpy(un.sun_path, "foo.socket"); if((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
err_sys("socket failed");
size = offsetof(struct sockaddr_un, sun_path) + strlen(un.sun_path);
if(bind(fd, (struct sockaddr *)&un, size) < 0)
err_sys("bind failed");
printf("UNIX domain socket bound\n");
exit(0);
}

当运行此程序时,bind请求成功执行,但是如果第二次运行该程序,则出错返回,其原因是该文件已经存在。在删去该文件之前,程序清单17-7不会成功执行。

确定绑定地址长度的方法是,先确定sun_path成员在sockaddr_un结构中的偏移量,然后将此与路径名长度(不包括终止null字符)相加。因为在sun_path之前的成员与实现相关,所以我们使用<stddef.h>头文件中的offsetof宏计算sun_path成员从结构开始处的偏移量。如果查看<stddef.h>,则可见到类似于下列形式的定义:

#define offsetof(TYPE, MEMBER)    ((int)&((TYPE *)0)->MEMBER)

假定该结构从地址0开始,此表达式求得成员起始地址的整型值。

2、唯一连接

服务器进程可以使用标准bind、listen和accept函数,为客户进程安排一个唯一的UNIX域连接(unique UNIX domain connection)。客户进程使用connect与服务器进程联系;服务器进程接受了connect请求后,在服务器进程和客户进程之间就存在了唯一连接。这种风格的操作与我们在http://www.cnblogs.com/nufangrensheng/p/3567376.html中的程序清单16-4和程序清单16-5中所示的对因特网域套接字的操作相同。

程序清单17-8 UNIX域套接字的serv_listen函数

#include "apue.h"
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h> #define QLEN 10 /*
* Create a server endpoint of a connection.
* Return fd if all ok, <0 on error.
*/
int
serv_listen(const char *name)
{
int fd, len, err, rval;
struct sockaddr_un un; /* create a UNIX domain stream socket */
if((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
return(-1);
unlink(name); /* in case it already exists */ /* fill in socket address structure */
memset(&un, 0, sizeof(un));
un.sun_family = AF_UNIX;
strcpy(un.sun_path, name);
len = offsetof(struct sockaddr_un, sun_path) + strlen(name); /* bind the name to the descriptor */
if(bind(fd, (struct sockaddr *)&un, len) < 0)
{
rval = -2;
goto errout;
}
if(listen(fd, QLEN) < 0) /* tell kernel we're a server */
{
rval = -3;
goto errout;
}
return(fd); errout:
err = errno;
close(fd);
errno = err;
return(rval);
}

首先,我们调用socket创建一个UNIX域套接字。然后将欲赋予套接字的众所周知路径名填入sockaddr_un结构。该结构是调用bind的参数。注意,我们不需要设置某些平台提供的sun_len字段,操作系统用传送给bind函数的地址长度设置该字段。

最后调用listen函数以通知内核进程该进程将作为服务器进程等待客户进程的连接请求。当收到一个客户进程的连接请求后,服务器进程调用serv_accept函数。

程序清单17-9 UNIX域套接字的serv_accept函数

#include "apue.h"
#include <sys/socket.h>
#include <sys/un.h>
#include <time.h>
#include <errno.h> #define STALE 30 /* client's name can't be older than this (sec) */ /*
* Wait for a client connection to arrive, and accept it.
* We also obtain the client's usr ID from the pathname
* that it must bind before calling us.
* Returns new fd if all ok, <0 on error
*/
int serv_accept(int listenfd, uid_t *uidptr)
{
int clifd, len, err, rval;
time_t staletime;
struct sockaddr_un un;
struct stat statbuf; len = sizeof(un);
if((clifd = accept(listenfd, (struct sockaddr *)&un, &len)) < 0)
return(-1); /* often errno=EINTR, if signal caught */ /* obtain the client's uid from its calling address */
len -= offsetof(struct sockaddr_un, sun_path); /* len of pathname */
un.sun_path[len] = 0; /* null terminate */ if(stat(un.sun_path, &statbuf) < 0)
{
rval = -2;
goto errout;
}
#ifdef S_ISSOCK /* not defined fro SVR4 */
if(S_ISSOCK(statbuf.st_mode) == 0)
{
rval = -3; /* not a socket */
goto errout;
}
#endif
if((statbuf.st_mode & (S_IRWXG | S_IRWXO)) ||
(statbuf.st_mode & S_IRWXU) != S_IRWXU)
{
rval = -4; /* is not rwx------ */
goto errout;
} staletime = time(NULL) - STALE;
if(statbuf.st_atime < staletime ||
statbuf.st_ctime < staletime ||
statbuf.st_mtime < staletime)
{
rval = -5; /* i-node is too old */
goto errout;
} if(uidptr != NULL)
*uidptr = statbuf.st_uid; /* return uid of caller */
unlink(un.sun_path); /* we're done with pathname now */
return(clifd); errout:
err = errno;
close(clifd);
errno = err;
return(rval);
}

服务器进程在调用serv_accept中阻塞以等待一客户进程调用cli_conn。从accept返回时,返回值是连接到客户进程的崭新的描述符。另外,accept函数也经由其第二个参数(指向sockaddr_un结构的指针)返回客户进程赋予其套接字的路径名(包含客户进程ID的名字)。接着,程序在此路径名结尾处填补null字符,然后调用stat函数。这使我们验证该路径名确实是一个套接字,其权限允许用户-读、用户-写及用户-执行。我们也验证与套接字相关联的3个时间不比当前时间早30秒。(time函数参考http://www.cnblogs.com/nufangrensheng/p/3507715.html)。

如若通过了所有这些检验,则可认为客户进程的身份(其有效用户ID)是该套接字的所有者。

客户进程调用cli_conn函数对联向服务器进程的连接进行初始化。

程序清单17-10 用于UNIX域套接字的cli_conn函数

#include "apue.h"
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h> #define CLI_PATH "/var/tmp/" /* +5 fro pid = 14 chars */
#define CLI_PERM S_IRWXU /* rwx for user only */ /*
* Create a client endpoint and connect to a server.
* Returns fd if all ok, <0 on error.
*/
int
cli_conn(const char *name)
{
int fd, len, err, rval;
struct sockaddr_un un; /* create a UNIX domain stream socket */
if((fd = socket(AF_UNIX, SOCK_STREM, 0)) < 0)
return(-1); /* fill socket address structure with our address */
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); /* in case it already exits */
if(bind(fd, (struct sockaddr *)&un, len) < 0)
{
rval = -2;
goto errout;
}
if(chmod(un.sun_path, CLI_PERM) < 0)
{
rval = -3;
goto errout;
} /* fill socket address structure with server's address */
memset(&un, 0, sizeof(un));
un.sun_family = AF_UNIX;
strcpy(un.sun_path, name);
len = offsetof(struct sockaddr_un, sun_path) + strlen(name); if(connect(fd, (struct sockaddr *)&un, len) < 0)
{
rval = -4;
goto errout;
}
return(fd); errout:
err = errno;
close(fd);
errno = err;
return(rval); }

我们调用socket函数创建UNIX域套接字的客户端进程,然后用客户端进程专有的名字填入sockaddr_un结构。

我们不让系统为我们选择一个默认的地址,原因是这样处理后,服务器进程不能区分各个客户进程。于是,我们绑定我们自己的地址,在开发使用套接字的客户端程序时通常并不采用这一步骤。

我们绑定的路径名的最后5个字符来自客户进程ID。我们调用unlink,以防该路径名已经存在,然后,调用bind将名字赋予客户进程套接字。这在文件系统中创建了一个套接字文件,所用的名字与被绑定的路径名一样。接着,调用chmod关闭除用户-读、用户-写以及用户-执行以外的其他权限。在serv_accept中,服务器进程检验这些权限以及套接字用户ID以验证客户进程的身份。

然后,我们必须填充另一个sockaddr_un结构,这次用的是服务器进程众所周知的路径名。最后,调用connect函数初始化与服务器进程的连接。

本篇博文内容摘自《UNIX环境高级编程》(第二版),仅作个人学习记录所用。关于本书可参考:http://www.apuebook.com/

高级进程间通信之UNIX域套接字的更多相关文章

  1. unix进程间通信方式(下)-unix域套接字(转)

    在之前的博客中已经总结了其它7种进程间的通信方式.unix域套接字用于在同一台计算机上的进程间通信,虽然因特网域套接字可用于同一目的,但是unix域套接字的效率更高.unix域套接字并不进行协议处理, ...

  2. UNIX网络编程——UNIX域套接字编程和socketpair 函数

    一.UNIX Domain Socket IPC socket API原本是为网络通讯设计的,但后来在socket的框架上发展出一种IPC机制,就是UNIX Domain Socket.虽然网络soc ...

  3. 通过UNIX域套接字传递描述符的应用

      传送文件描述符是高并发网络服务编程的一种常见实现方式.Nebula 高性能通用网络框架即采用了UNIX域套接字传递文件描述符设计和实现.本文详细说明一下传送文件描述符的应用. 1. TCP服务器程 ...

  4. UNIX 域套接字——UNIX domain socket

    /*********************程序相关信息********************* * 程序编号:015 * 程序编写起始日期:2013.11.30 * 程序编写完成日期:2013.1 ...

  5. UNIX域套接字编程和socketpair 函数

    一.UNIX Domain Socket IPC socket API原本是为网络通讯设计的,但后来在socket的框架上发展出一种IPC机制,就是UNIX Domain Socket.虽然网络soc ...

  6. Unix域套接字简介

    在Linux系统中,有很多进程间通信方式,套接字(Socket)就是其中的一种.但传统的套接字的用法都是基于TCP/IP协议栈的,需要指定IP地址.如果不同主机上的两个进程进行通信,当然这样做没什么问 ...

  7. Unix域套接字(Unix Domain Socket)介绍【转】

    本文转载自:http://blog.csdn.net/roland_sun/article/details/50266565 版权声明:本文为博主原创文章,未经博主允许不得转载. 在Linux系统中, ...

  8. UNIX域套接字(unix domain)

    UNIX域套接字用于在同一台机器上运行的进程之间的通信. UNIX域套接字提供流和数据报两种接口. 说明:UNIX域套接字比因特网套接字效率更高.它仅赋值数据:不进行协议处理,如添加或删除网络报头.计 ...

  9. 《网络编程》Unix 域套接字

    概述 Unix 域套接字是一种client和server在单主机上的 IPC 方法.Unix 域套接字不运行协议处理,不须要加入或删除网络报头,无需验证和,不产生顺序号,无需发送确认报文,比因特网域套 ...

随机推荐

  1. TopFreeTheme精选免费模板【20130703】

    今天我们给大家分享13个最新的主题模板,5款WordPress主题,5款Joomla模板,3款OpenCart主题. BowThemes – BT Folio v1.0 Template for Jo ...

  2. 创建被访问的swf文件

    首先创建一个fla文件,名字叫movie.fla,在该文件库中放一个mc, 并将其拖放到舞台上,然后 命名为test_mc, 然后在库中给该mc绑定一个类,类名随意. 创建访问swf文件的swf文件 ...

  3. [cocos2d-js]chipmunk例子(二)

    ; ; ; ; <<; var NOT_GRABABLE_MASK = ~GRABABLE_MASK_BIT; var MainLayer = cc.Layer.extend({ _bal ...

  4. dom 侧栏分享

    <!doctype html> <html> <head> <meta charset="utf-8"> <title> ...

  5. javascript压缩图片无刷新上传

    首先要说明,该方式我没有实现成功,主要问题点是压缩后能拿到压缩后图片地址,确不知道怎么上传上去,如果有哪位大牛可以指点一二,万分感谢. 图片压缩代码: compress: function(sourc ...

  6. android 开发必用的开源库

    LogReport:  https://github.com/wenmingvs/LogReport,   崩溃日志上传框架 wcl-permission-demo:Android 6.0 - 动态权 ...

  7. MVC中使用AuthorizeAttribute做身份验证操作

    代码顺序为:OnAuthorization-->AuthorizeCore-->HandleUnauthorizedRequest 如果AuthorizeCore返回false时,才会走H ...

  8. 第三百二十七天 how can I 坚持

    都没心情学习了,睡觉.太失败了.  好了,你赢了,最怕女人不说话了,我妈一生气就不说话,有点怕我妈,你想删就把我删了吧,我不怪你. 给你个善意的建议,任何事情都要有度,过犹而不及,你是属于那种比较听家 ...

  9. windows下揪出java程序占用cpu很高的线程 并找到问题代码 死循环线程代码

    我的一个java程序偶尔会出现cpu占用很高的情况 一直不知道什么原因 今天终于抽时间解决了 系统是win2003 jvisualvm 和 jconsole貌似都只能看到总共占用的cpu 看不到每个线 ...

  10. 利用预渲染加速iOS设备的图像显示

    最近在做一个UITableView的例子,发现滚动时的性能还不错.但来回滚动时,第一次显示的图像不如再次显示的图像流畅,出现前会有稍许的停顿感.于是我猜想显示过的图像肯定是被缓存起来了,查了下文档后发 ...