Linux下的IPC-UNIX Domain Socket【转】
本文转载自:http://blog.csdn.net/guxch/article/details/7041052
一、 概述
UNIX Domain Socket是在socket架构上发展起来的用于同一台主机的进程间通讯(IPC),它不需要经过网络协议栈,不需要打包拆包、计算校验和、维护序号和应答等,只是将应用层数据从一个进程拷贝到另一个进程。UNIX Domain Socket有SOCK_DGRAM或SOCK_STREAM两种工作模式,类似于UDP和TCP,但是面向消息的UNIX Domain Socket也是可靠的,消息既不会丢失也不会顺序错乱。
UNIX Domain Socket可用于两个没有亲缘关系的进程,是全双工的,是目前使用最广泛的IPC机制,比如X Window服务器和GUI程序之间就是通过UNIX Domain Socket通讯的。
二、工作流程
UNIX Domain socket与网络socket类似,可以与网络socket对比应用。
上述二者编程的不同如下:
- address family为AF_UNIX
- 因为应用于IPC,所以UNIXDomain socket不需要IP和端口,取而代之的是文件路径来表示“网络地址”。这点体现在下面两个方面。
- 地址格式不同,UNIXDomain socket用结构体sockaddr_un表示,是一个socket类型的文件在文件系统中的路径,这个socket文件由bind()调用创建,如果调用bind()时该文件已存在,则bind()错误返回。
- UNIX Domain Socket客户端一般要显式调用bind函数,而不象网络socket一样依赖系统自动分配的地址。客户端bind的socket文件名可以包含客户端的pid,这样服务器就可以区分不同的客户端。
UNIX Domain socket的工作流程简述如下(与网络socket相同)。
服务器端:创建socket—绑定文件(端口)—监听—接受客户端连接—接收/发送数据—…—关闭
客户端:创建socket—绑定文件(端口)—连接—发送/接收数据—…—关闭
三、阻塞和非阻塞(SOCK_STREAM方式)
读写操作有两种操作方式:阻塞和非阻塞。
1.阻塞模式下
阻塞模式下,发送数据方和接收数据方的表现情况如同命名管道,参见本人文章“Linux下的IPC-命名管道的使用(http://blog.csdn.NET/guxch/article/details/6828452)”
2.非阻塞模式
在send或recv函数的标志参数中设置MSG_DONTWAIT,则发送和接收都会返回。如果没有成功,则返回值为-1,errno为EAGAIN 或 EWOULDBLOCK。
四、测试代码
服务器端
- #include <stdio.h>
- #include <sys/stat.h>
- #include <sys/socket.h>
- #include <sys/un.h>
- #include <errno.h>
- #include <stddef.h>
- #include <string.h>
- // the max connection number of the server
- #define MAX_CONNECTION_NUMBER 5
- /* * Create a server endpoint of a connection. * Returns fd if all OK, <0 on error. */
- int unix_socket_listen(const char *servername)
- {
- int fd;
- struct sockaddr_un un;
- if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
- {
- return(-1);
- }
- int len, rval;
- unlink(servername); /* in case it already exists */
- memset(&un, 0, sizeof(un));
- un.sun_family = AF_UNIX;
- strcpy(un.sun_path, servername);
- len = offsetof(struct sockaddr_un, sun_path) + strlen(servername);
- /* bind the name to the descriptor */
- if (bind(fd, (struct sockaddr *)&un, len) < 0)
- {
- rval = -2;
- }
- else
- {
- if (listen(fd, MAX_CONNECTION_NUMBER) < 0)
- {
- rval = -3;
- }
- else
- {
- return fd;
- }
- }
- int err;
- err = errno;
- close(fd);
- errno = err;
- return rval;
- }
- int unix_socket_accept(int listenfd, uid_t *uidptr)
- {
- int clifd, len, 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);
- }
- /* 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;
- }
- else
- {
- if (S_ISSOCK(statbuf.st_mode) )
- {
- if (uidptr != NULL) *uidptr = statbuf.st_uid; /* return uid of caller */
- unlink(un.sun_path); /* we're done with pathname now */
- return clifd;
- }
- else
- {
- rval = -3; /* not a socket */
- }
- }
- int err;
- err = errno;
- close(clifd);
- errno = err;
- return(rval);
- }
- void unix_socket_close(int fd)
- {
- close(fd);
- }
- int main(void)
- {
- int listenfd,connfd;
- listenfd = unix_socket_listen("foo.sock");
- if(listenfd<0)
- {
- printf("Error[%d] when listening...\n",errno);
- return 0;
- }
- printf("Finished listening...\n",errno);
- uid_t uid;
- connfd = unix_socket_accept(listenfd, &uid);
- unix_socket_close(listenfd);
- if(connfd<0)
- {
- printf("Error[%d] when accepting...\n",errno);
- return 0;
- }
- printf("Begin to recv/send...\n");
- int i,n,size;
- char rvbuf[2048];
- for(i=0;i<2;i++)
- {
- //===========接收==============
- size = recv(connfd, rvbuf, 804, 0);
- if(size>=0)
- {
- // rvbuf[size]='\0';
- printf("Recieved Data[%d]:%c...%c\n",size,rvbuf[0],rvbuf[size-1]);
- }
- if(size==-1)
- {
- printf("Error[%d] when recieving Data:%s.\n",errno,strerror(errno));
- break;
- }
- /*
- //===========发送==============
- memset(rvbuf, 'c', 2048);
- size = send(connfd, rvbuf, 2048, 0);
- if(size>=0)
- {
- printf("Data[%d] Sended.\n",size);
- }
- if(size==-1)
- {
- printf("Error[%d] when Sending Data.\n",errno);
- break;
- }
- */
- sleep(30);
- }
- unix_socket_close(connfd);
- printf("Server exited.\n");
- }
客户端代码
- #include <stdio.h>
- #include <stddef.h>
- #include <sys/stat.h>
- #include <sys/socket.h>
- #include <sys/un.h>
- #include <errno.h>
- #include <string.h>
- /* Create a client endpoint and connect to a server. Returns fd if all OK, <0 on error. */
- int unix_socket_conn(const char *servername)
- {
- int fd;
- if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) /* create a UNIX domain stream socket */
- {
- return(-1);
- }
- int len, rval;
- struct sockaddr_un un;
- memset(&un, 0, sizeof(un)); /* fill socket address structure with our address */
- un.sun_family = AF_UNIX;
- sprintf(un.sun_path, "scktmp%05d", getpid());
- len = offsetof(struct sockaddr_un, sun_path) + strlen(un.sun_path);
- unlink(un.sun_path); /* in case it already exists */
- if (bind(fd, (struct sockaddr *)&un, len) < 0)
- {
- rval= -2;
- }
- else
- {
- /* fill socket address structure with server's address */
- memset(&un, 0, sizeof(un));
- un.sun_family = AF_UNIX;
- strcpy(un.sun_path, servername);
- len = offsetof(struct sockaddr_un, sun_path) + strlen(servername);
- if (connect(fd, (struct sockaddr *)&un, len) < 0)
- {
- rval= -4;
- }
- else
- {
- return (fd);
- }
- }
- int err;
- err = errno;
- close(fd);
- errno = err;
- return rval;
- }
- void unix_socket_close(int fd)
- {
- close(fd);
- }
- int main(void)
- {
- srand((int)time(0));
- int connfd;
- connfd = unix_socket_conn("foo.sock");
- if(connfd<0)
- {
- printf("Error[%d] when connecting...",errno);
- return 0;
- }
- printf("Begin to recv/send...\n");
- int i,n,size;
- char rvbuf[4096];
- for(i=0;i<10;i++)
- {
- /*
- //=========接收=====================
- size = recv(connfd, rvbuf, 800, 0); //MSG_DONTWAIT
- if(size>=0)
- {
- printf("Recieved Data[%d]:%c...%c\n",size,rvbuf[0],rvbuf[size-1]);
- }
- if(size==-1)
- {
- printf("Error[%d] when recieving Data.\n",errno);
- break;
- }
- if(size < 800) break;
- */
- //=========发送======================
- memset(rvbuf,'a',2048);
- rvbuf[2047]='b';
- size = send(connfd, rvbuf, 2048, 0);
- if(size>=0)
- {
- printf("Data[%d] Sended:%c.\n",size,rvbuf[0]);
- }
- if(size==-1)
- {
- printf("Error[%d] when Sending Data:%s.\n",errno,strerror(errno));
- break;
- }
- sleep(1);
- }
- unix_socket_close(connfd);
- printf("Client exited.\n");
- }
五、 讨论
通过实际测试,发现UNIXDomain Socket与命名管道在表现上有很大的相似性,例如,UNIX Domain Socket也会在磁盘上创建一个socket类型文件;如果读端进程关闭了,写端进程“写数据”时,有可能使进程异常退出,等等。查阅有关文档,摘录如下:
Send函数
当调用该函数时,send先比较待发送数据的长度len和套接字s的发送缓冲的 长度,如果len大于s的发送缓冲区的长度,该函数返回SOCKET_ERROR;如果len小于或者等于s的发送缓冲区的长度,那么send先检查协议是否正在发送s的发送缓冲中的数据,如果是就等待协议把数据发送完,如果协议还没有开始发送s的发送缓冲中的数据或者s的发送缓冲中没有数据,那么 send就比较s的发送缓冲区的剩余空间和len,如果len大于剩余空间大小send就一直等待协议把s的发送缓冲中的数据发送完,如果len小于剩余空间大小send就仅仅把buf中的数据copy到剩余空间里(注意并不是send把s的发送缓冲中的数据传到连接的另一端的,而是协议传的,send仅仅是把buf中的数据copy到s的发送缓冲区的剩余空间里)。如果send函数copy数据成功,就返回实际copy的字节数,如果send在copy数据时出现错误,那么send就返回SOCKET_ERROR;如果send在等待协议传送数据时网络断开的话,那么send函数也返回SOCKET_ERROR。
要注意send函数把buf中的数据成功copy到s的发送缓冲的剩余空间里后它就返回了,但是此时这些数据并不一定马上被传到连接的另一端。如果协议在后续的传送过程中出现网络错误的话,那么下一个Socket函数就会返回SOCKET_ERROR。(每一个除send外的Socket函数在执行的最开始总要先等待套接字的发送缓冲中的数据被协议传送完毕才能继续,如果在等待时出现网络错误,那么该Socket函数就返回 SOCKET_ERROR)
注意:在Unix系统下,如果send在等待协议传送数据时网络断开的话,调用send的进程会接收到一个SIGPIPE信号,进程对该信号的默认处理是进程终止。
Recv函数与send类似,看样子系统在实现各种IPC时,有些地方是复用的。
Linux下的IPC-UNIX Domain Socket【转】的更多相关文章
- libpqxx接口的在linux下的使用,解决psql:connections on Unix domain socket "/tmp/.s.PGSQL.5432"错误
在项目中使用postgresql数据库时要求在windows和linux双平台兼容.于是在windows下使用的接口在linux下爆出异常: psql:connections on Unix doma ...
- 由一个简单需求到Linux环境下的syslog、unix domain socket
本文记录了因为一个简单的日志需求,继而对linux环境下syslog.rsyslog.unix domain socket的学习.本文关注使用层面,并不涉及rsyslog的实现原理,感兴趣的读者可以参 ...
- linux一切皆文件之Unix domain socket描述符(二)
一.知识准备 1.在linux中,一切皆为文件,所有不同种类的类型都被抽象成文件(比如:块设备,socket套接字,pipe队列) 2.操作这些不同的类型就像操作文件一样,比如增删改查等 3.主要用于 ...
- Unix domain socket IPC
UNIX Domain socket 虽然网络socket也可用于同一台主机的进程间通讯(通过lo地址127.0.0.1),但是unix domain socket用于IPC更有效率:不需要经过网络协 ...
- Unix domain socket 简介
Unix domain socket 又叫 IPC(inter-process communication 进程间通信) socket,用于实现同一主机上的进程间通信.socket 原本是为网络通讯设 ...
- Unix domain socket
转载:http://www.cnblogs.com/chekliang/p/3222950.html socket API原本是为网络通讯设计的,但后来在socket的框架上发展出一种IPC机制,就是 ...
- (unix domain socket)使用udp发送>=128K的消息会报ENOBUFS的错误
一个困扰我两天的问题, Google和Baidu没有找到解决方法! 此文为记录这个问题,并给出原因和解决方法. 1.Unix domain socket简介 unix域协议并不是一个实际的协议族,而是 ...
- Envoy 基础教程:使用 Unix Domain Socket(UDS) 与上游集群通信
Envoy Proxy 在大多数情况下都是作为 Sidecar 与应用部署在同一网络环境中,每个应用只需要与 Envoy(localhost)交互,不需要知道其他服务的地址.然而这并不是 Envoy ...
- ndk学习16: unix domain socket
一.UNIX Domain Socket 概念: UNIX Domain Socket是在socket架构上发展起来的用于同一台主机的进程间通讯(IPC) 特点: 1. 它不需要经过网络协议栈,不需要 ...
随机推荐
- redis的安装和使用【2】redis的java操作
修改redis.conf# 配置绑定ip,作者机子为192.168.100.192,请读者根据实际情况设置bind 192.168.100.192#非保护模式protected-mode no保存重启 ...
- CAD绘制一个图象标记对象(com接口VB语言)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 3 ...
- 梦想3D控件 2018.7.26更新
下载地址: http://www.mxdraw.com/ndetail_108.html 1. 编写所有接口函数使用的CHM文档 2. 增加交互绘制功能 3. 增加案例弧形窗建模案例 4. 增 ...
- PHP爬数据 QueryList
QueryList官方文档:https://www.querylist.cc/docs/guide/v3 因为php版本使用5.6,所以使用QueryList v3版本,php7可以使用 v4版本 v ...
- 04Oracle Database 登陆
Oracle Database 登陆 EM Express Login https://localhost:5500/em/login cmd sqlplus SQL/PLUS system/code ...
- HDU多校Round 5
Solved:3 rank:71 E. Everything Has Changed #include <bits/stdc++.h> using namespace std; const ...
- UVA - 1620 Lazy Susan(逆序数)
题目: 把1~n(n≤500)放到一个圆盘里,每个数恰好出现一次.每次可以选4个连续的数字翻转顺序.问能不能变成1.2.3....n的顺序. 思路: 这样的题的规律真的是一点都不好推,看了网上的博客知 ...
- PHP 锁机制
应用环境 解决高并发,库存为负数的情况 阻塞模式 如果其他进程已经加锁文件,当前进程会一直等其他进程解锁文件后继续执行 flock($fp, LOCK_EX) // 文件锁 非阻塞模式 如果其他进程已 ...
- Centos下Yum安装PHP5.5,5.6,7.0及扩展
默认的版本太低了,手动安装有一些麻烦,想采用Yum安装的可以使用下面的方案: 1.检查当前安装的PHP包 yum list installed | grep php 如果有安装的PHP包,先删除他们 ...
- 08.C语言:特殊函数
C语言:特殊函数 1.递归函数: 与普通函数比较,执行过程不同,该函数内部调用它自己,它的执行必须要经过两个阶段:递推阶段,回归阶段: 当不满足回归条件,不再递推: #include <stdi ...