一、UNIX Domain Socket IPC

socket
API原本是为网络通讯设计的,但后来在socket的框架上发展出一种IPC机制,就是UNIX Domain
Socket。虽然网络socket也可用于同一台主机的进程间通讯(通过loopback地址127.0.0.1),但是UNIX Domain
Socket用于IPC更有效率:不需要经过网络协议栈,不需要打包拆包、计算校验和、维护序号和应答等,只是将应用层数据从一个进程拷贝到另一个进程。UNIX域套接字与TCP套接字相比较,在同一台主机的传输速度前者是后者的两倍。这是因为,IPC机制本质上是可靠的通讯,而网络协议是为不可靠的通讯设计的。UNIX
Domain Socket也提供面向流和面向数据包两种API接口,类似于TCP和UDP,但是面向消息的UNIX Domain
Socket也是可靠的,消息既不会丢失也不会顺序错乱。

使用UNIX Domain
Socket的过程和网络socket十分相似,也要先调用socket()创建一个socket文件描述符,address
family指定为AF_UNIX,type可以选择SOCK_DGRAM或SOCK_STREAM,protocol参数仍然指定为0即可。

UNIX Domain Socket与网络socket编程最明显的不同在于地址格式不同,用结构体sockaddr_un表示,网络编程的socket地址是IP地址加端口号,而UNIX Domain Socket的地址是一个socket类型的文件在文件系统中的路径,这个socket文件由bind()调用创建,如果调用bind()时该文件已存在,则bind()错误返回。

#define UNIX_PATH_MAX    108

struct sockaddr_un {
sa_family_t
sun_family;               /* AF_UNIX */
char sun_path[UNIX_PATH_MAX];  /* pathname */
};

二、回射/客户服务器程序

通信的流程跟前面说过的tcp/udp 是类似的,下面直接来看程序:

 C++ Code 
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
 
/*************************************************************************
    > File Name: echoser_tcp.c
    > Author: Simba
    > Mail: dameng34@163.com
    > Created Time: Sun 03 Mar 2013 06:13:55 PM CST
 ************************************************************************/

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<string.h>
#include<sys/un.h>

#define ERR_EXIT(m) \
    do { \
        perror(m); \
        exit(EXIT_FAILURE); \
    } while (0)

void echo_ser(int conn)
{
    char recvbuf[1024];
    int n;
    while (1)
    {

memset(recvbuf, 0, sizeof(recvbuf));
        n = read(conn, recvbuf, sizeof(recvbuf));
        if (n == -1)
        {
            if (n == EINTR)
                continue;

ERR_EXIT("read error");
        }

else if (n == 0)
        {
            printf("client close\n");
            break;
        }

fputs(recvbuf, stdout);
        write(conn, recvbuf, strlen(recvbuf));
    }

close(conn);
}

/* unix domain socket与TCP套接字相比较,在同一台主机的传输速度前者是后者的两倍。*/
int main(void)
{
    int listenfd;
    if ((listenfd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
        ERR_EXIT("socket error");

unlink("/tmp/test socket"); //地址复用
    struct sockaddr_un servaddr;
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sun_family = AF_UNIX;
    strcpy(servaddr.sun_path, "/tmp/test socket");

if (bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)
        ERR_EXIT("bind error");

if (listen(listenfd, SOMAXCONN) < 0)
        ERR_EXIT("listen error");

int conn;
    pid_t pid;

while (1)
    {

conn = accept(listenfd, NULL, NULL);
        if (conn == -1)
        {

if (conn == EINTR)
                continue;
            ERR_EXIT("accept error");
        }

pid = fork();
        if (pid == -1)
            ERR_EXIT("fork error");
        if (pid == 0)
        {
            close(listenfd);
            echo_ser(conn);
            exit(EXIT_SUCCESS);
        }

close(conn);
    }

return 0;
}

 C++ Code 
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
 
/*************************************************************************
    > File Name: echocli_tcp.c
    > Author: Simba
    > Mail: dameng34@163.com
    > Created Time: Sun 03 Mar 2013 06:13:55 PM CST
 ************************************************************************/

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<string.h>
#include<sys/un.h>

#define ERR_EXIT(m) \
    do { \
        perror(m); \
        exit(EXIT_FAILURE); \
    } while (0)

void echo_cli(int conn)
{
    char sendbuf[1024] = {0};
    char recvbuf[1024] = {0};
    while (fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
    {

write(conn, sendbuf, strlen(sendbuf));
        read(conn, recvbuf, sizeof(recvbuf));
        fputs(recvbuf, stdout);
        memset(recvbuf, 0, sizeof(recvbuf));
        memset(sendbuf, 0, sizeof(sendbuf));
    }

close(conn);
}

int main(void)
{
    int sock;
    if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
        ERR_EXIT("socket error");

struct sockaddr_un servaddr;
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sun_family = AF_UNIX;
    strcpy(servaddr.sun_path, "/tmp/test socket");

if (connect(sock, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)
        ERR_EXIT("connect error");

echo_cli(sock);

return 0;
}

server 使用fork 的形式来接受多个连接,server调用bind 会创建一个文件,如下所示:

simba@ubuntu:~/Documents/code/linux_programming/UNP/socket$ ls -l /tmp/test\ socket 
srwxrwxr-x 1 simba simba 0 Jun 12 15:27 /tmp/test socket

即文件类型为s,表示SOCKET文件,与FIFO(命名管道)文件,类型为p,类似,都表示内核的一条通道,读写文件实际是在读写内核通道。程序中调用unlink(解除硬链接) 是为了在开始执行程序时删除以前创建的文件,以便在重启服务器时不会提示address in use。其他方面与以前说过的回射客户服务器程序没多大区别,不再赘述。

三、UNIX域套接字编程注意点

1、bind成功将会创建一个文件,权限为0777 & ~umask
2、sun_path最好用一个绝对路径
3、UNIX域协议支持流式套接口与报式套接口
4、UNIX域流式套接字connect发现监听队列满时,会立刻返回一个ECONNREFUSED,这和TCP不同,如果监听队列满,会忽略到来的SYN,这导致对方重传SYN。

四、socketpair 函数

功能:创建一个全双工的流管道
原型 int socketpair(int domain, int type, int protocol, int sv[2]);
参数
domain: 协议家族
type: 套接字类型
protocol: 协议类型
sv: 返回套接字对
返回值:成功返回0;失败返回-1

实际上socketpair 函数跟pipe 函数是类似的,也只能在同个主机上具有亲缘关系的进程间通信,但pipe 创建的匿名管道是半双工的,而socketpair 可以认为是创建一个全双工的管道。

可以使用socketpair 创建返回的套接字对进行父子进程通信:

 C++ Code 
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
 
/*************************************************************************
    > File Name: echoser.c
    > Author: Simba
    > Mail: dameng34@163.com
    > Created Time: Fri 01 Mar 2013 06:15:27 PM CST
 ************************************************************************/

#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<unistd.h>
#include<stdlib.h>
#include<errno.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<string.h>

#define ERR_EXIT(m) \
    do { \
        perror(m); \
        exit(EXIT_FAILURE); \
    } while (0)

int main(void)
{
    int sockfds[2];

if (socketpair(PF_UNIX, SOCK_STREAM, 0, sockfds) < 0)
        ERR_EXIT("sockpair");

pid_t pid;
    pid = fork();
    if (pid == -1)
        ERR_EXIT("fork");

if (pid > 0)
    {
        int val = 0;
        close(sockfds[1]);
        while (1)
        {

++val;
            printf(" sending data: %d\n", val);
            write(sockfds[0], &val, sizeof(val));
            read(sockfds[0], &val, sizeof(val));
            printf("recv data : %d\n", val);
            sleep(1);
        }

}

else if (pid == 0)
    {

int val;
        close(sockfds[0]);
        while (1)
        {

read(sockfds[1], &val, sizeof(val));
            ++val;
            write(sockfds[1], &val, sizeof(val));
        }
    }

return 0;
}

输出如下:

simba@ubuntu:~/Documents/code/linux_programming/UNP/socket$ ./socketpair 
 sending data: 1
recv data : 2
 sending data: 3
recv data : 4
 sending data: 5
recv data : 6
 sending data: 7
recv data : 8
 sending data: 9
recv data : 10
 sending data: 11
recv data : 12
 sending data: 13
recv data : 14
 sending data: 15
recv data : 16
...................................

即父进程持有sockfds[0] 套接字进行读写,而子进程持有sockfds[1] 套接字进行读写。

参考:

《Linux C 编程一站式学习》

《TCP/IP详解 卷一》

《UNP》

UNIX域套接字编程和socketpair 函数的更多相关文章

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

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

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

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

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

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

  4. UNIX网络编程——通过UNIX域套接字传递描述符和 sendmsg/recvmsg 函数

    在前面我们介绍了UNIX域套接字编程,更重要的一点是UNIX域套接字可以在同一台主机上各进程之间传递文件描述符. 下面先来看两个函数: #include <sys/types.h> #in ...

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

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

  6. 通过UNIX域套接字传递描述符和 sendmsg/recvmsg 函数

    在前面我们介绍了UNIX域套接字编程,更重要的一点是UNIX域套接字可以在同一台主机上各进程之间传递文件描述符. 下面先来看两个函数: #include <sys/types.h>  #i ...

  7. unix域套接字UDP网络编程

    unix域套接字UDP网络编程,服务器如下面: #include <stdio.h> #include <stdlib.h> #include <string.h> ...

  8. 高级进程间通信之UNIX域套接字

    UNIX域套接字用于在同一台机器上运行的进程之间的通信.虽然因特网域套接字可用于同一目的,但UNIX域套接字的效率更高.UNIX域套接字仅仅复制数据:它们并不执行协议处理,不需要添加或删除网络报头,无 ...

  9. UNIX域套接字(unix domain)

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

随机推荐

  1. Android音乐播放-MediaPlayer

    当你坐公交无聊的时候,当你淹没在地铁中人潮中的时候,你是否想内心保持一份的安静呢,那么请带上耳机,打开你的音乐播放器,听一首老歌带你进入宁静的世界,音乐播放这个功能在智能手机出现之前,诺基亚时代,甚至 ...

  2. Python3 写Windows Service服务程序

    用Python开发Windows Service,用Python来做这个事情必须要借助第三方模块pywin32,下载路径:https://pypi.org/project/pywin32/#files ...

  3. Linux上安装Bugzilla4.4小记

    因项目需要,我受命在一台Linux服务器上搭建一个Bugzilla,进过一天的调试,这项任务总算完成了.现在可以肯定的说,安装过程不复杂,基本就是解压,填参数,执行命令.Bugzilla要跑起来,本机 ...

  4. ubuntu apache2 虚拟主机服务

    ubuntu apache2 虚拟主机服务 本次配置的是一个 ip 对应多个 虚拟主机 1:先检查 ubuntu server 是否已经安装了 apache2 web服务: apache2 -v 看到 ...

  5. 微信小程序-配置解答

    微信小程序启动页面: Pages:  index / logs 有 index和logs的页面,每个页面中都有独立的js逻辑,wxml负责页面内容,wxss负责样式. utils app.js app ...

  6. PyQt5教程——菜单和工具栏(3)

    PyQt5中的菜单和工具栏 在这部分的PyQt5教程中,我们将创建菜单和工具栏.菜单式位于菜单栏的一组命令操作.工具栏是应用窗体中由按钮和一些常规命令操作组成的组件. 主窗口 QMainWindow类 ...

  7. java数组转json

    public String toJsonObject(String[] list) { String json="["; for (int i=0;i<list.length ...

  8. ActiveMq C#客户端 消息队列的使用(存和取)

    1.准备工具 VS2013Apache.NMS.ActiveMQ-1.7.2-bin.zipapache-activemq-5.14.0-bin.zip 2.开始项目 VS2013新建一个C#控制台应 ...

  9. 源代码解说ActionBar的各种使用方法

    1. Navigation Drawer 很多应用程序都使用了Navigation Drawer,如网易邮箱client.该控件位于 android.support.v4.widget.DrawerL ...

  10. 转载【TP3.2】:使用PHP生成二维码

    转载:在网上down了一个二维码插件PHPQRcode,整合到了ThinkPHP 3.2.3,然后写了个外部自定义函数直接调用生成二维码,根据参数不同有不同尺寸效果,整合其实挺简单,分享给大家! 今天 ...