Unix domain socket IPC
UNIX Domain socket
虽然网络socket也可用于同一台主机的进程间通讯(通过lo地址127.0.0.1),但是unix domain socket用于IPC更有效率:不需要经过网络协议栈,不需要打包拆包/计算校验和/维护信号和应答等。只是将应用层数据从一个进程拷贝到另一个进程。这是因为IPC机制本质上是可靠的通讯,而网络协议是不可靠的通讯。
unix domain socket也提供面向流和面向数据的两种API接口,类似TCP和UDP,但是面向消息的unix domain socket也是可靠的,消息既不会丢失也不会顺序错乱。
socket:address family指定为AF_UNIX,type可以选择SOCK_DGRAM或SOCK_STREAM,protocol参数仍指定为0即可。
unix domain socket的地址格式用sockaddr_un表示,指定一个socket类型文件在文件系统中的路径。这个socket文件有bind()调用创建,如果调用bind()时该文件已存在,则bind()错误返回。
unix domain socket客户端一般要显示调用bind(),而不是依赖系统自动分配的地址。客户端bind一个自己指定的socket文件名的好处是,该文件可以包含客户端的pid以便服务器区分不同的客户端。
time + pid
sprintf(cli_addr.sun_path, "%u.%u.sock", time(NULL), getpid()).
注:客户端与服务器端各自绑定自己的文件,文件名必须不同(两端的文件名没有联系)。
地址格式,摘自man unix:
A UNIX domain socket address is represented in the following structure:
#define UNIX_PATH_MAX 108
struct sockaddr_un {
sa_family_t sun_family; /* AF_UNIX */
char sun_path[UNIX_PATH_MAX]; /* pathname */
};
sun_family always contains AF_UNIX.
Three types of address are distinguished in this structure:
* pathname: a UNIX domain socket can be bound to a null-terminated file system pathname using
bind(2). When the address of the socket is returned by getsockname(2), getpeername(2), and
accept(2), its length is offsetof(struct sockaddr_un, sun_path) + strlen(sun_path) + 1, and
sun_path contains the null-terminated pathname.
* unnamed: A stream socket that has not been bound to a pathname using bind(2) has no name. Like?
wise, the two sockets created by socketpair(2) are unnamed. When the address of an unnamed socket
is returned by getsockname(2), getpeername(2), and accept(2), its length is sizeof(sa_family_t),
and sun_path should not be inspected.
* abstract: an abstract socket address is distinguished by the fact that sun_path[0] is a null byte
('\0'). The socket's address in this namespace is given by the additional bytes in sun_path that
are covered by the specified length of the address structure. (Null bytes in the name have no spe‐
cial significance.) The name has no connection with file system pathnames. When the address of an
abstract socket is returned by getsockname(2), getpeername(2), and accept(2), the returned addrlen
is greater than sizeof(sa_family_t) (i.e., greater than 2), and the name of the socket is contained
in the first (addrlen - sizeof(sa_family_t)) bytes of sun_path. The abstract socket namespace is a
nonportable Linux extension.
例程一(UDP):
// UDP server.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/un.h>
#include <sys/socket.h>
#include <sys/stat.h> #define BUF_SIZE 10 #define DES_PATH "/tmp/main.socket" int main(int argc, char *argv[])
{
int sd;
struct sockaddr_un un, peer_un;
socklen_t len;
int i;
int ret;
char buf[BUF_SIZE]; sd = socket(AF_UNIX, SOCK_DGRAM, );
if(sd < )
{
perror("socket");
return -;
} unlink(DES_PATH);
memset(&un, , sizeof(struct sockaddr_un));
un.sun_family = AF_UNIX;
strncpy(un.sun_path, DES_PATH, sizeof(un.sun_path) - );
ret = bind(sd, (struct sockaddr *)&un, sizeof(struct sockaddr_un));
if(ret < )
{
perror("bind");
return -;
} while()
{
memset(buf, , BUF_SIZE);
// len = offsetof(struct sockaddr_un, sun_path) + strlen(un.sun_path) + 1;
len = sizeof(struct sockaddr_un);
ret = recvfrom(sd, buf, BUF_SIZE, , (struct sockaddr *)&peer_un, &len); if(ret > )
{
printf("Recvfrom [%d] bytes from >>%s:\n", ret, peer_un.sun_path);
for(i = ; i < BUF_SIZE; i++)
{
printf("0x%.2x\t", 0xFF&buf[i]);
if( == (i + ) % )
{
printf("\n");
}
}
} else {
printf("Recvfrom [%d]\n", ret);
}
} close(sd); return ;
}
// UDP client
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stddef.h>
#include <fcntl.h>
#include <time.h>
#include <sys/un.h>
#include <sys/socket.h>
#include <sys/stat.h> int main(int argc, char *argv[])
{
int sd;
struct sockaddr_un un;
socklen_t len;
int tnode;
int ret ; if(argc < )
{
return -;
} tnode = atoi(argv[]);
sd = socket(AF_UNIX, SOCK_DGRAM, );
if(sd < )
{
perror("socket");
return -;
} memset(&un, , sizeof(struct sockaddr_un));
un.sun_family = AF_UNIX;
// strcpy(un.sun_path, "/tmp/main.socket");
snprintf(un.sun_path, sizeof(struct sockaddr_un), "%u.%u.sock", time(NULL), getpid()); printf("sockaddr is %s\n", un.sun_path);
ret = bind(sd, (struct sockaddr *)&un, sizeof(struct sockaddr_un));
if(ret < )
{
perror("bind");
return -;
} memset(&un, , sizeof(struct sockaddr_un));
un.sun_family = AF_UNIX;
strcpy(un.sun_path, "/tmp/main.socket"); len = offsetof(struct sockaddr_un, sun_path) + strlen(un.sun_path) + ; sendto(sd, &tnode, sizeof(int), , (struct sockaddr *)&un, len); close(sd); return ;
}
运行结果:
~$./c
sockaddr is 1480428387.3096.sock
~$./c
sockaddr is 1480428390.3097.sock
~$./c
sockaddr is 1480428392.3099.sock
~$./c
sockaddr is 1480428395.3100.sock
~$./c
sockaddr is 1480428398.3101.sock
~$./c
sockaddr is 1480428409.3103.sock
~$./s
Recvfrom [] bytes from >>1480428387.3096.sock:
0x64 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00
Recvfrom [] bytes from >>1480428390.3097.sock:
0xe8 0x03 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00
Recvfrom [] bytes from >>1480428392.3099.sock:
0x10 0x27 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00
Recvfrom [] bytes from >>1480428395.3100.sock:
0xa0 0x86 0x01 0x00 0x00
0x00 0x00 0x00 0x00 0x00
Recvfrom [] bytes from >>1480428398.3101.sock:
0x41 0x42 0x0f 0x00 0x00
0x00 0x00 0x00 0x00 0x00
Recvfrom [] bytes from >>1480428409.3103.sock:
0x64 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00
如果在server端读数据前延迟一段时间如10s,在client端一个sock多次sendto相同数据,server读取数据仍然和client发送包数量一致并且接收数据一致,可知udp每读取一次都是一包数据,无需做分包处理。
UDP是面向消息的协议,每个UDP段都是一条消息,应用程序必须以消息为单位提取数据,不能一次提取任意字节的数据。
UDP每读取一次都是一包数据(UDP已做分包处理)。
TCP需要做分包处理,具体事例可参考文档“TCP&UDP”。
注意:TCP只能与接入它的客户端通信,客户端必须与服务器绑定后才能相互通信。
UDP服务器(准确的说,是UDP端)可以与任意客户端通信,且两者之间可以不建立联系就可直接发送信息。
例程二(TCP):
// unix_server.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h> #define SOCK_NAME "/tmp/echo.server"
#define LISTEN_BACKLOG 50
#define BUF_SIZE 1024 #define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while() int main(int argc, char *argv[])
{
int sfd = , cfd = ;
int i = , ret = ;
struct sockaddr_un my_addr, peer_addr;
socklen_t peer_addr_size;
char buf[BUF_SIZE] = {}; sfd = socket(AF_UNIX, SOCK_STREAM, );
if(sfd < ){
// printf("%s socket error.\n", SOCK_NAME);
handle_error("socket");
} unlink(SOCK_NAME);
memset(&my_addr, , sizeof(struct sockaddr_un));
my_addr.sun_family = AF_UNIX;
strncpy(my_addr.sun_path, SOCK_NAME, sizeof(my_addr.sun_path)-);
if(bind(sfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr_un)) == -){
handle_error("bind");
} if(listen(sfd, LISTEN_BACKLOG) == -){
handle_error("listen");
} signal(SIGCHLD, SIG_IGN);
while(){ peer_addr_size = sizeof(struct sockaddr_un);
cfd = accept(sfd, (struct sockaddr *)&peer_addr, &peer_addr_size);
if(cfd < ){
handle_error("accept");
}else {
ret = fork();
if(ret < ){
handle_error("fork");
} else if(ret == ){
while(){
ret = read(cfd, buf, sizeof(buf));
buf[ret] = ;
printf("%s (len %d) recv %d bytes: %s\n", peer_addr.sun_path, peer_addr_size, ret, buf);
for(i = ; i < ret; i++){
if(buf[i] >= 'a' && buf[i] <='z')
buf[i] += 'A'-'a';
}
write(cfd, buf, ret);
}
}
}
} return ;
}
// unix_client.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stddef.h> #define SER_NAME "/tmp/echo.server"
#define SOCK_NAME_PRE "/tmp/ECHO" #define BUF_SIZE 1024 #define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while() int main(int argc, char *argv[])
{
int cfd = ;
int ret = ;
struct sockaddr_un client_addr, server_addr;
char buf[BUF_SIZE]={};
int len = ; cfd = socket(AF_UNIX, SOCK_STREAM, );
if(cfd < ){
handle_error("socket");
} memset(&client_addr, , sizeof(struct sockaddr_un));
client_addr.sun_family = AF_UNIX;
snprintf(client_addr.sun_path, sizeof(struct sockaddr_un), "%s.%d.%ld", SOCK_NAME_PRE, getpid(), time(NULL));
len = offsetof(struct sockaddr_un, sun_path) + strlen(client_addr.sun_path) + ;
printf("client addr:%s, len:%d\n", client_addr.sun_path, len);
ret = bind(cfd, (struct sockaddr *)&client_addr, sizeof(struct sockaddr_un));
if(ret < ){
handle_error("bind");
} memset(&server_addr, , sizeof(struct sockaddr_un));
server_addr.sun_family = AF_UNIX;
strncpy(server_addr.sun_path, SER_NAME, sizeof(struct sockaddr_un));
len = offsetof(struct sockaddr_un, sun_path) + strlen(server_addr.sun_path) + ;
// ret = connect(cfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr_un));
ret = connect(cfd, (struct sockaddr *)&server_addr, len);
if(ret < ){
handle_error("connect");
} while(){
printf("please input the bytes:\n");
scanf("%s", buf);
ret = strlen(buf);
buf[ret] = ;
write(cfd, buf, ret+);
ret = read(cfd, buf, sizeof(buf));
if(ret >=) ret = ;
buf[ret]=;
printf("conversion %d bytes:[%s]\n", ret, buf);
} return ;
}
该例程实现echo回显且小写变大写功能。
本地套接字通过curl用HTTP协议访问:
curl --unix-socket /var/run/docker.sock -X GET http:/v1./containers/json
curl --unix-socket /var/run/docker.sock -X GET http:/containers/json
Unix domain socket IPC的更多相关文章
- Unix domain socket 简介
Unix domain socket 又叫 IPC(inter-process communication 进程间通信) socket,用于实现同一主机上的进程间通信.socket 原本是为网络通讯设 ...
- Linux下的IPC-UNIX Domain Socket【转】
本文转载自:http://blog.csdn.net/guxch/article/details/7041052 一. 概述 UNIX Domain Socket是在socket架构上发展起来的用于同 ...
- ndk学习16: unix domain socket
一.UNIX Domain Socket 概念: UNIX Domain Socket是在socket架构上发展起来的用于同一台主机的进程间通讯(IPC) 特点: 1. 它不需要经过网络协议栈,不需要 ...
- 由一个简单需求到Linux环境下的syslog、unix domain socket
本文记录了因为一个简单的日志需求,继而对linux环境下syslog.rsyslog.unix domain socket的学习.本文关注使用层面,并不涉及rsyslog的实现原理,感兴趣的读者可以参 ...
- mysql unix domain socket and network socket, ssh key
当主机填写为localhost时mysql会采用 unix domain socket连接 当主机填写为127.0.0.1时mysql会采用tcp方式连接 这是linux套接字网络的特性,win平台不 ...
- Unix domain socket
转载:http://www.cnblogs.com/chekliang/p/3222950.html socket API原本是为网络通讯设计的,但后来在socket的框架上发展出一种IPC机制,就是 ...
- 【转】PHP实现系统编程(四)--- 本地套接字(Unix Domain Socket)
原文:http://blog.csdn.net/zhang197093/article/details/78143687?locationNum=6&fps=1 --------------- ...
- (unix domain socket)使用udp发送>=128K的消息会报ENOBUFS的错误
一个困扰我两天的问题, Google和Baidu没有找到解决方法! 此文为记录这个问题,并给出原因和解决方法. 1.Unix domain socket简介 unix域协议并不是一个实际的协议族,而是 ...
- php, hhvm与odp & Unix domain Socket方式
接上一篇,复习一下 启动php或hhvm: php/sbin/php-fpm start hhvm/bin/hhvm_control start 启动nginx或lighttpd: webserver ...
随机推荐
- [C++基础]那些容易被混淆的概念:函数/数组指针-指针函数/数组,类/函数模板-模板类/函数
函数指针-指针函数 函数指针的重点是指针.表示的是一个指针,它指向的是一个函数.eg: int (*pf)(); 指针函数的重点是函数.表示的是一个函数,它的返回值是指针.eg: int* fun() ...
- Patterns-Observer
http://book.javanb.com/java-design-patterns/index.html Java深入到一定程度,就不可避免的碰到设计模式(design pattern)这一概念, ...
- 深入理解javascript闭包【整理】
原文链接:http://www.cn-cuckoo.com/2007/08/01/understand-javascript-closures-72.html 英文原文:http://www.jibb ...
- Windows下Hadoop编程环境配置指南
刘勇 Email: lyssym@sina.com 本博客记录作者在工作与研究中所经历的点滴,一方面给自己的工作与生活留下印记,另一方面若是能对大家有所帮助,则幸甚至哉矣! 简介 鉴于最近在研究 ...
- Linux命令-权限管理命令:umask
umask -S 显示用户创建目录或文件时的默认权限 mkdir shuaige 创建一个shuaige目录 ls -ld shuaige 查看shuaige目录当前的权限(和上面默认的权限是一样的) ...
- Linux命令-权限管理命令:chgrp
groupadd shuaige 创建一个用户组名字叫shuaige ls -l /home/wangyunpeng/abcd 查看abcd文件的权限 chgrp shuaige /home/wang ...
- Android Native IPC 方案支持情况
Binder - 不支持Native层的binder 内存共享 - 不支持 信号量(信号灯) - 不支持 消息队列 - 不支持 信号 - 支持,但是不能用sigqueue传消息,只能用来安装信号,可以 ...
- C语言错误处理方法、C++异常处理方法(throw, try, catch)简介
一.C语言错误处理方法 1.返回值(if … else语句判断错误) 2.errno(linux 系统调用) 3.goto语句(函数内局部跳转) 4.setjmp.longjmp(Do not use ...
- const与#define、结构体对齐、函数重载name mangling、new/delete 等
一.bool 类型 逻辑型也称布尔型,其取值为true(逻辑真)和false(逻辑假),存储字节数在不同编译系统中可能有所不同,VC++中为1个字节. 声明方式:bool result; result ...
- for循环和增强版的for循环
增强的for循环. 缺点: 对于数组.不能方便的訪问下标值. 对于集合,与使用Interator相比.不能方便的删除集合中的内容(在内部也是调用Interator). 除了简单遍历并读取当中的 ...