UNIX Domain Socket IPC

来源:互联网 发布:万方专利数据库 编辑:程序博客网 时间:2024/04/28 02:30

目录

  • 目录
  • 概述
  • socket函数使用
    • struct sockaddr_un
    • socket
    • bind
    • listen
    • accept
    • connect
  • Socket IPC 实例
    • server
    • client
    • 运行结果


概述

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

虽然网络socket也可用于同一台主机的进程间通讯(通过loopback地址127.0.0.1),但是UNIX Domain Socket用于IPC更有效率:

  • 不需要经过网络协议栈。
  • 不需要打包拆包
  • 不需要计算校验和
  • 不需要维护序号和应答等

这是因为IPC机制本质上是可靠的通讯,而网络协议是为不可靠的通讯设计的。UNIX Domain Socket也提供面向流和面向数据包两种API接口,类似TCP和UDP,但是面向流的UNIX Domain Socket也是可靠的,消息既不会丢失也不会顺序错乱。

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

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


socket函数使用

接下来,把UNIX Domain Socket编程用到的几个主要函数讲解一下。

struct sockaddr_un

UNIX Domain Socket使用的地址结构体定义如下:

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

socket()

#include <sys/types.h>#include <sys/socket.h>int socket(int domain, int type, int protocol);

参数定义如下:

  1. domain指定socket的通信协议集(AF_UNIX和AF_INET等)

    • AF_UNIX(与AF_LOCAL和AF_FILE相同)只能够用于单一的unix的系统上,使用AF_UNIX会在系统上创建一个socket文件,不同进程通过读写这个文件来实现通信。
    • AF_INET则是用于网络的,所以可以允许远程主机之间的通信。
  2. type指定socket的类型(SOCK_STREAM和SOCK_DGRAM等)

    • SOCK_STREAM表明使用TCP协议,这就意味着会提供按顺序的、可靠、双向、面向连接的比特流。
    • SOCK_DGRAM表明使用UDP协议,这就意味着会提供定长的、不可靠、无连接的通信。
  3. protocol指定实际使用的传输协议。最常见的就是IPPROTO_TCP、IPPROTO_STCP、IPPROTO_UDP、IPPROTO_DCCP。如果该项为0,即根据选定的domain和type选择使用缺省协议。

调用socket()函数后,成功会返回文件描述符,失败则返回-1。


bind()

#include <sys/types.h>#include <sys/socket.h>int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

bind()为socket分配地址。当使用socket()创建套接字之后,仅仅赋予了其所使用的协议,而没有分配地址。在接受其它主机连接前,必须先调用bind()为socket分配地址。

参数定义如下:

  1. sockfd:这是socket的描述符。
  2. addr:指向sockaddr结构(用于表示所分配地址)的指针。
  3. addrlen:sockaddr结构的长度。

listen()

#include <sys/types.h>#include <sys/socket.h>int listen(int sockfd, int backlog);

当socket和一个地址绑定后,调用listen()函数开始监听连接请求。但是,这只能在有可靠数据流保证时使用,如SOCK_STREAM。

参数定义如下:

  1. sockfd:socket的描述符。
  2. backlog:一个决定监听队列大小的整数,当有一个连接请求到来,就会进入此监听队列。当队列满后,新的连接请求就会返回错误。

如果监听成功返回0,否则返回-1。


accept()

#include <sys/types.h>#include <sys/socket.h>int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

当应用程序监听来自其他主机的面向数据流的连接时,通过事件(比如UNIX select()系统调用)通知它。必须用accept()函数初始化连接。accept()为每个连接创立新的套接字并从监听队列中移除这个连接。

参数定义如下:

  1. sockfd:监听的socket描述符。
  2. addr:指向sockaddr结构体的指针,客户端地址信息。
  3. addrlen:指向socklen_t的指针,确定客户端结构体的大小。

accpet成功返回新的套接字描述符,出错返回-1。进一步通信必须通过这个套接字。


connect()

#include <sys/types.h>#include <sys/socket.h>int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

connect()系统调用为一个套接字设置连接,参数有文件描述符和主机地址。


Socket IPC 实例

接下来,简单实现一个进程间通过UNIX Domain Socket进行通讯的参考实例。

server

#include <stdlib.h>#include <stdio.h>#include <string.h>#include <stddef.h>#include <sys/types.h>#include <sys/socket.h>#include <sys/un.h>#include <unistd.h>#define QLEN 8char *socket_path="wzy.socket";int main(int argc, char **argv){    int fd, clifd, n;    struct sockaddr_un un;    char buf[100];    if (argc > 1) socket_path = argv[1];    if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {        perror("socket error");        exit(1);    }    memset(&un, 0, sizeof(un));    un.sun_family = AF_UNIX;    strncpy(un.sun_path, socket_path, sizeof(un.sun_path) - 1);    unlink(socket_path);    if (bind(fd, (struct sockaddr *) &un, sizeof(un)) < 0) {        perror("bind error");        exit(1);    }    printf("UNIX Domain Socket bound\n");    memset(&buf, 0, sizeof(buf));    if (listen(fd, QLEN) < 0) {        perror("listen error");        exit(1);    }    while (1) {        if ((clifd = accept(fd, NULL, NULL)) == -1) {            perror("accpet error");            continue;        }        while ((n = read(clifd, buf, sizeof(buf))) > 0) {            printf("read %d bytes: %s\n", n, buf);        }        if (n == -1) {            exit(-1);        } else if (n == 0) {            printf("END\n");            close(clifd);        }    }    return 0;}

client

#include <stdlib.h>#include <stdio.h>#include <string.h>#include <stddef.h>#include <sys/types.h>#include <sys/socket.h>#include <sys/un.h>#include <unistd.h>char *socket_path="wzy.socket";int main(int argc, char **argv){    int fd;    struct sockaddr_un un;    char buf[100];    if (argc > 1) socket_path = argv[1];    if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {        perror("socket error");        exit(1);    }    memset(&un, 0, sizeof(un));    un.sun_family = AF_UNIX;    strncpy(un.sun_path, socket_path, sizeof(un.sun_path) - 1);    if (connect(fd, (struct sockaddr *) &un, sizeof(un)) < 0) {        perror("connect error");        exit(-1);    }    while (scanf("%s", buf) != EOF) {        if (write(fd, buf, sizeof(buf)) < 0) {            perror("write error");            exit(-1);        }    }    return 0;}

运行结果

server监听客户端发送的数据:
server

client上传的数据如下:
client

1 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 用遥控器关了电视打不开怎么办 遥控器一个按键坏了怎么办 电视用遥控器关的打不开怎么办 电动车遥控器按键坏了怎么办 海尔空调遥控器按键坏了怎么办 汽车手机支架吸盘吸不住怎么办 车载手机支架吸盘坏了怎么办 假牙的吸盘坏了怎么办 燃气费用一直未交怎么办 凌度gps模块无法定位怎么办? 放疗定位线掉了怎么办 被网络平台骗了怎么办 手机重力传感器坏了怎么办 锤子手机重力传感器坏了怎么办 平板电脑没有开关键怎么办 手机重力感应器坏了怎么办 苹果手机重力感应器坏了怎么办 苹果手机陀螺仪坏了怎么办 狗狗的爪子肿了怎么办 压缩文件之后显示拒绝访问怎么办 压缩文件解压后全散开了怎么办 dnf助手改名字用完了怎么办 缅甸 佤邦 办中国护照 怎么办? 电脑玩游戏网络延迟大怎么办 qq好友空间锁了怎么办 卡盟进货额不足怎么办 被朋友骗了钱怎么办 联通在学校网差怎么办 前夫把我微信拉黑 孩子的事怎么办 微信好友验证疑似被盗怎么办 我的世界被banip怎么办 dnf深渊宝珠出了怎么办 吞噬魔4个球吃了怎么办 dnf没有支援兵了怎么办 家里没通天然气怎么办 苹果6p16g不够用怎么办 魅蓝手机内存不够用怎么办 3dmax灯光全黑怎么办 高压15o低压1oo怎么办 源码一位乘法中c怎么办 怀孕搬了重东西怎么办