Unix domain socket 又叫 IPC(inter-process communication 进程间通信) socket,用于实现同一主机上的进程间通信。socket 原本是为网络通讯设计的,但后来在 socket 的框架上发展出一种 IPC 机制,就是 UNIX domain socket。虽然网络 socket 也可用于同一台主机的进程间通讯(通过 loopback 地址 127.0.0.1),但是 UNIX domain socket 用于 IPC 更有效率:不需要经过网络协议栈,不需要打包拆包、计算校验和、维护序号和应答等,只是将应用层数据从一个进程拷贝到另一个进程。这是因为,IPC 机制本质上是可靠的通讯,而网络协议是为不可靠的通讯设计的。
UNIX domain socket 是全双工的,API 接口语义丰富,相比其它 IPC 机制有明显的优越性,目前已成为使用最广泛的 IPC 机制,比如 X Window 服务器和 GUI 程序之间就是通过 UNIX domain socket 通讯的。
Unix domain socket 是 POSIX 标准中的一个组件,所以不要被名字迷惑,linux 系统也是支持它的。

下面通过一个简单的 demo 来理解相关概念。程序分为服务器端和客户端两部分,它们之间通过 unix domain socket 进行通信。

服务器端程序

下面是一个非常简单的服务器端程序,它从客户端读字符,然后将每个字符转换为大写并回送给客户端:

#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h> #define MAXLINE 80 char *socket_path = "server.socket"; int main(void)
{
struct sockaddr_un serun, cliun;
socklen_t cliun_len;
int listenfd, connfd, size;
char buf[MAXLINE];
int i, n; if ((listenfd = socket(AF_UNIX, SOCK_STREAM, )) < ) {
perror("socket error");
exit();
} memset(&serun, , sizeof(serun));
serun.sun_family = AF_UNIX;
strcpy(serun.sun_path, socket_path);
size = offsetof(struct sockaddr_un, sun_path) + strlen(serun.sun_path);
unlink(socket_path);
if (bind(listenfd, (struct sockaddr *)&serun, size) < ) {
perror("bind error");
exit();
}
printf("UNIX domain socket bound\n"); if (listen(listenfd, ) < ) {
perror("listen error");
exit();
}
printf("Accepting connections ...\n"); while() {
cliun_len = sizeof(cliun);
if ((connfd = accept(listenfd, (struct sockaddr *)&cliun, &cliun_len)) < ){
perror("accept error");
continue;
} while() {
n = read(connfd, buf, sizeof(buf));
if (n < ) {
perror("read error");
break;
} else if(n == ) {
printf("EOF\n");
break;
} printf("received: %s", buf); for(i = ; i < n; i++) {
buf[i] = toupper(buf[i]);
}
write(connfd, buf, n);
}
close(connfd);
}
close(listenfd);
return ;
}

简单介绍一下这段代码:

int socket(int family, int type, int protocol);

使用 UNIX domain socket 的过程和网络 socket 十分相似,也要先调用 socket() 创建一个 socket 文件描述符.
family 指定为 AF_UNIX,使用 AF_UNIX 会在系统上创建一个 socket 文件,不同进程通过读写这个文件来实现通信。
type 可以选择 SOCK_DGRAM 或 SOCK_STREAM。SOCK_STREAM 意味着会提供按顺序的、可靠、双向、面向连接的比特流。SOCK_DGRAM 意味着会提供定长的、不可靠、无连接的通信。
protocol 参数指定为 0 即可。
UNIX domain socket 与网络 socket 编程最明显的不同在于地址格式不同,用结构体 sockaddr_un 表示,网络编程的 socket 地址是 IP 地址加端口号,而 UNIX domain socket 的地址是一个 socket 类型的文件在文件系统中的路径,这个 socket 文件由 bind() 调用创建,如果调用 bind() 时该文件已存在,则 bind() 错误返回。因此,一般在调用 bind() 前会检查 socket 文件是否存在,如果存在就删除掉。
网络 socket 编程类似,在 bind 之后要 listen,表示通过 bind 的地址(也就是 socket 文件)提供服务。
接下来必须用 accept() 函数初始化连接。accept() 为每个连接创立新的套接字并从监听队列中移除这个连接。

客户端程序

下面是客户端程序,它接受用户的输入,并把字符串发送给服务器,然后接收服务器返回的字符串并打印:

#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
#include <string.h>
#include <unistd.h> #define MAXLINE 80 char *client_path = "client.socket";
char *server_path = "server.socket"; int main() {
struct sockaddr_un cliun, serun;
int len;
char buf[];
int sockfd, n; if ((sockfd = socket(AF_UNIX, SOCK_STREAM, )) < ){
perror("client socket error");
exit();
} // 一般显式调用bind函数,以便服务器区分不同客户端
memset(&cliun, , sizeof(cliun));
cliun.sun_family = AF_UNIX;
strcpy(cliun.sun_path, client_path);
len = offsetof(struct sockaddr_un, sun_path) + strlen(cliun.sun_path);
unlink(cliun.sun_path);
if (bind(sockfd, (struct sockaddr *)&cliun, len) < ) {
perror("bind error");
exit();
} memset(&serun, , sizeof(serun));
serun.sun_family = AF_UNIX;
strcpy(serun.sun_path, server_path);
len = offsetof(struct sockaddr_un, sun_path) + strlen(serun.sun_path);
if (connect(sockfd, (struct sockaddr *)&serun, len) < ){
perror("connect error");
exit();
} while(fgets(buf, MAXLINE, stdin) != NULL) {
write(sockfd, buf, strlen(buf));
n = read(sockfd, buf, MAXLINE);
if ( n < ) {
printf("the other side has been closed.\n");
}else {
write(STDOUT_FILENO, buf, n);
}
}
close(sockfd);
return ;
}

与网络 socket 编程不同的是,UNIX domain socket 客户端一般要显式调用 bind 函数,而不依赖系统自动分配的地址。客户端 bind 一个自己指定的 socket 文件名的好处是,该文件名可以包含客户端的 pid 等信息以便服务器区分不同的客户端。

运行上面的程序

分别把服务器端程序和客户端程序保存为 server.c 和 client.c 文件,并编译:

$ gcc server.c -o server
$ gcc client.c -o client

先启动服务器端程序,然后启动客户端程序输入字符串并回车:

还不错,客户端得到了服务器端返回的大写字符串。接下来看看当前目录下的文件:

哈哈,多了两个 socket 文件。

总结

Unix domain socket 主要用于同一主机上的进程间通信。与主机间的进程通信不同,它不是通过 "IP地址 + TCP或UDP端口号" 的方式进程通信,而是使用 socket 类型的文件来完成通信,因此在稳定性、可靠性以及效率方面的表现都很不错。

参考:
UNIX Domain Socket IPC
[linux] unix domain socket 例子

Unix domain socket 简介的更多相关文章

  1. (unix domain socket)使用udp发送>=128K的消息会报ENOBUFS的错误

    一个困扰我两天的问题, Google和Baidu没有找到解决方法! 此文为记录这个问题,并给出原因和解决方法. 1.Unix domain socket简介 unix域协议并不是一个实际的协议族,而是 ...

  2. 网络协议之:socket协议详解之Unix domain Socket

    目录 简介 什么是Unix domain Socket 使用socat来创建Unix Domain Sockets 使用ss命令来查看Unix domain Socket 使用nc连接到Unix do ...

  3. 问题解决:psql: could not connect to server: No such file or directory Is the server running locally and accepting connections on Unix domain socket "/var/run/postgresql/.s.PGSQL.5432"?

    错误提示: psql: could not connect to server: No such file or directory Is the server running locally and ...

  4. ndk学习16: unix domain socket

    一.UNIX Domain Socket 概念: UNIX Domain Socket是在socket架构上发展起来的用于同一台主机的进程间通讯(IPC) 特点: 1. 它不需要经过网络协议栈,不需要 ...

  5. Unix Domain Socket 域套接字实现

    主要注意流程: STREAM SOCKET: Server :  socket() --->  bind() ---> listen()  ---> accept() Client: ...

  6. 由一个简单需求到Linux环境下的syslog、unix domain socket

    本文记录了因为一个简单的日志需求,继而对linux环境下syslog.rsyslog.unix domain socket的学习.本文关注使用层面,并不涉及rsyslog的实现原理,感兴趣的读者可以参 ...

  7. libpqxx接口的在linux下的使用,解决psql:connections on Unix domain socket "/tmp/.s.PGSQL.5432"错误

    在项目中使用postgresql数据库时要求在windows和linux双平台兼容.于是在windows下使用的接口在linux下爆出异常: psql:connections on Unix doma ...

  8. UNIX DOMAIN SOCKET效率

    关于UNIX DOMAIN SOCKET和普通udp socket的对比 在TX1(4核A57 1.7GHz)的板卡上进行测试,每个包大小设置为1024,全速收发,UDS的速度在90Mbps左右,UD ...

  9. nginx、php-fpm默认配置与性能–TCP socket还是unix domain socket【转】

    原文地址:https://www.cnxct.com/default-configuration-and-performance-of-nginx-phpfpm-and-tcp-socket-or-u ...

随机推荐

  1. SQL Server 从2000复制数据到2008及以上版本的一种方法

    1.通过Linked Servers 执行sql出现错误提示,无法执行复制数据操作. sql: insert into tb_User select from [**.**.*.**].DB.dbo. ...

  2. 转:js小技巧 ,将彻底屏蔽鼠标右键,可用于Table ,取消选取、防止复制,IE地址栏前换成自己的图标

    1. oncontextmenu="window.event.returnValue=false" 将彻底屏蔽鼠标右键<table border oncontextmenu= ...

  3. 【转】Java学习---内存泄露与溢出的区别

    Java内存泄露与溢出的区别 Java内存泄漏就是没有及时清理内存垃圾,导致系统无法再给你提供内存资源(内存资源耗尽): 而Java内存溢出就是你要求分配的内存超出了系统能给你的,系统不能满足需求,于 ...

  4. Active Directory、Exchange、单点登录,企业账号统一管理解决方案

    现在的公司一般都会有很多内部管理系统,比如OA.ERP.CRM.邮件系统等.员工入职之后如果每个系统都创建一个账号和密码,首先员工记系统账号就是一件非常头疼的事情,如果公司有一百个系统那就得创建一百个 ...

  5. 了解注解及java提供的几个基本注解

    先通过@SuppreessWarnings的应用让大家直观地了解注解: 通过System.runFinalizersOnExit(true);的编译器警告引出           @SuppressW ...

  6. header头

    <?php header('HTTP/1.1 200 OK'); // ok 正常访问 header('HTTP/1.1 404 Not Found'); //通知浏览器 页面不存在 heade ...

  7. Docker Java应用日志时间和容器时间不一致

    1.在docker容器和系统时间不一致是因为docker容器的原生时区为0时区,而国内系统为东八区. 2.还有容器中运行的java应用打出的日志时间和通过date -R方式获取的容器标准时间有八个小时 ...

  8. hadoop学习通过虚拟机安装hadoop完全分布式集群

    要想深入的学习hadoop数据分析技术,首要的任务是必须要将hadoop集群环境搭建起来,可以将hadoop简化地想象成一个小软件,通过在各个物理节点上安装这个小软件,然后将其运行起来,就是一个had ...

  9. Const vs. Readonly

    Const 被const修饰的变量不能为静态,因为const实际隐式上已经是静态变量. const变量在声明时就必须进行初始化,否则会有编译错误. const变量的赋值是发生在编译期间 Readonl ...

  10. jenkins slave 安装服务与卸载

    查看windows 服务 cmd 运行下图用我已经安装的jnlp服务展示效果 : services.msc 关于这个服务名称怎么来的: 我的 工作台路径:如下: 进入jenkins slave 下载j ...