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);
参数定义如下:
domain指定socket的通信协议集(AF_UNIX和AF_INET等)
- AF_UNIX(与AF_LOCAL和AF_FILE相同)只能够用于单一的unix的系统上,使用AF_UNIX会在系统上创建一个socket文件,不同进程通过读写这个文件来实现通信。
- AF_INET则是用于网络的,所以可以允许远程主机之间的通信。
type指定socket的类型(SOCK_STREAM和SOCK_DGRAM等)
- SOCK_STREAM表明使用TCP协议,这就意味着会提供按顺序的、可靠、双向、面向连接的比特流。
- SOCK_DGRAM表明使用UDP协议,这就意味着会提供定长的、不可靠、无连接的通信。
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分配地址。
参数定义如下:
- sockfd:这是socket的描述符。
- addr:指向sockaddr结构(用于表示所分配地址)的指针。
- addrlen:sockaddr结构的长度。
listen()
#include <sys/types.h>#include <sys/socket.h>int listen(int sockfd, int backlog);
当socket和一个地址绑定后,调用listen()函数开始监听连接请求。但是,这只能在有可靠数据流保证时使用,如SOCK_STREAM。
参数定义如下:
- sockfd:socket的描述符。
- 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()为每个连接创立新的套接字并从监听队列中移除这个连接。
参数定义如下:
- sockfd:监听的socket描述符。
- addr:指向sockaddr结构体的指针,客户端地址信息。
- 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监听客户端发送的数据:
client上传的数据如下:
- UNIX Domain Socket IPC
- UNIX Domain Socket IPC
- Unix Domain Socket IPc
- UNIX Domain Socket IPC
- UNIX Domain Socket IPC
- UNIX Domain Socket IPC
- UNIX Domain Socket IPC
- UNIX Domain Socket IPC
- UNIX Domain Socket IPC
- UNIX Domain Socket IPC
- UNIX Domain Socket IPC
- UNIX Domain Socket IPC
- UNIX Domain Socket IPC
- UNIX Domain Socket IPC (sockaddr_un )
- UNIX Domain Socket IPC (sockaddr_un )
- UNIX Domain Socket IPC (sockaddr_un )
- UNIX Domain Socket IPC (sockaddr_un )
- UNIX Domain Socket IPC (sockaddr_un )
- 各大型SMTP服务器及端口收集
- 为什么要使用数据库连接池
- C++ Primer Plus第六版编程练习10.4解答
- Redis 高可用集群管理工具Sentinel
- 有关Spring事务的传播特性
- UNIX Domain Socket IPC
- 类,方法,成员变量,局部变量的可用修饰符
- 01 WebAPI中Common类和Controller类的定义
- 黑马程序员-JAVASE入门(抽象类、继承、模板设计模式)
- CMD命令名详细大全
- GPG入门教程
- ubuntu英文环境下安装中文输入法
- Redis 日志收集系统高可用搭建
- 02 Web Host方式寄宿WebAPI