socket IPC
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是全双工的,API接口语义丰富,相比其它IPC机制有明显的优越性,目前已成为使用最广泛的IPC机制,比如X Window服务器和GUI程序之间就是通过UNIXDomain Socket通讯的。
UNIX Domain Socket与网络socket编程最明显的不同在于地址格式不同,用结构体sockaddr_un表示,网络编程的socket地址是IP地址加端口号,而UNIX Domain Socket的地址是一个socket类型的文件在文件系统中的路径,这个socket文件由bind()调用创建,如果调用bind()时该文件已存在,则bind()错误返回。
对比网络套接字
int socket(int domain, int type, int protocol);
参数:
domain:AF_UNIX、 AF_LOCAL
type:SOCK_STREAM、SOCK_DGRAM
套接字地址结构:
struct sockaddr_in
{
__kernel_sa_family_t **sin_family**; /* Address family */ 地址结构类型
__be16 **sin_port**; /* Port number */ 端口号
struct in_addr **sin_addr**; /* Internet address */ IP地址
};
struct sockaddr_un
{
__kernel_sa_family_t **sun_family**; /* AF_UNIX */ 地址结构类型
char **sun_path**[UNIX_PATH_MAX]; /* pathname */ socket文件名(含路径)
};
网络套接字 | 本地套接字 | |
---|---|---|
函数参数 | domain:AF_INET type:SOCK_STREAM、SOCK_DGRAM | domain:AF_UNIX、 AF_LOCAL type:SOCK_STREAM、SOCK_DGRAM |
地址结构 | sockaddr_in | sockaddr_un |
地址结构初始化 | struct sockaddr_in srv_addr srv_addr.sin_family=AF_INET srv_addr. sin port=htons(8888); srv_addr.sinaddr.s_addr=htonl(INADDR_ANY) bind(fd(structsockaddr*)&srv_addr,sizeof(srv_addr)) | struct sockaddr_un srv_addr srv_addr.sun_family=AF_UNIX/ AF_LOCAL strcpy(srv_addr. sun path,“srv. socket”) len=brtsetof(struct sockaddr un, sium path+strlen(“srv. socket”) bind(fd,(struct sockaddr *)& srv_addr,1en) |
bind函数 | 调用bind函数 | bind函数调用成功会创建一个套接字,保证bind绑定成功,在此之前使用unlink函数 |
客户端 | 依赖“隐式绑定” | 客户端不能依赖“隐式绑定”,在建立通信过程中创建并初始化两个地址结构。 |
本地套接字实现通信
通信流程
服务端程序
#include<iostream>
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/un.h>
#include <ctype.h>
#include <arpa/inet.h>
#include "wrap.h"
using namespace std;
//定义一个端口号
#define SERVER_PORT 9527
//#define BUFSIZ _IO_BUFSIZ
#define SERVER_ADDR "server.socket"
int main (int argc,char *argv[])
{
int link_fd=0;//用于建立连接的文件描述符
int cfd=0;//用于与服务器进行通信
int ret,len,size;
char buf[BUFSIZ];//4096
//创建套接字
link_fd=Socket(AF_UNIX,SOCK_STREAM,0);
//检查返回值,判断是否创建成功
if(link_fd==-1)
{
sys_err("socket error");
}
//创建服务端、客户端结构体 初始化成员
struct sockaddr_un server_addr,client_addr;//引入头文件#include<netinet/in.h>
//socklen_t client_addrlen;
bzero(&server_addr,sizeof(server_addr));
server_addr.sun_family=AF_UNIX;
strcpy(server_addr.sun_path,SERVER_ADDR);
//求出(本地套接字)绑定地址结构长度(2字节)宏函数求解
len =offsetof(struct sockaddr_un,sun_path)+strlen(server_addr.sun_path);
//调用unlink函数保证创建成功
unlink(SERVER_ADDR);
//调用bind函数绑定
ret=bind(link_fd,(struct sockaddr*)&server_addr,len);
//判断是否绑定成功
if(ret==-1)
{
sys_err("bind error");
}
//设置同时与服务器建立连接的上限
Listen(link_fd,128);
cout<<("accept...")<<endl;
while(1)
{
len =sizeof(client_addr);
cfd=accept(link_fd,(struct sockaddr*)&client_addr,(socklen_t*)&len);
//打印通信的文件名
len-=offsetof(struct sockaddr_un,sun_path); //获得文件名的长度的,【需要减去地址结构体前两个字节】
client_addr.sun_path[len]='\0';
cout<<"client bind filename "<<client_addr.sun_path<<endl;
while((size=read(cfd,buf,sizeof(buf)))>0)
{
for(int i=0;i<size;i++)
{
buf[i]=toupper(buf[i]);
}
write(cfd,buf,size);
}
close(cfd);
}
close(link_fd);
return 0;
}
客户端程序
#include<iostream>
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <string.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <sys/un.h>
#include <stddef.h>
#include "wrap.h"
using namespace std;
//定义一个端口号(服务端)
#define SERVER_PORT 9527
#define SERVER_ADDR "server.socket"
#define CLIENT_ADDR "client.socket"
int main (int argc,char *argv[])
{
int cfd, len;
struct sockaddr_un servaddr, cliaddr;
char buf[4096];
cfd = Socket(AF_UNIX, SOCK_STREAM, 0);
bzero(&cliaddr, sizeof(cliaddr));
//客户端地址结构体成员初始化 ,用于创建套接字文件,绑定
cliaddr.sun_family = AF_UNIX;
strcpy(cliaddr.sun_path,CLIENT_ADDR);
//求出首部长度字节加上文件长度
len = offsetof(struct sockaddr_un, sun_path) + strlen(cliaddr.sun_path); /* 计算客户端地址结构有效长度 */
unlink(CLIENT_ADDR);
bind(cfd, (struct sockaddr *)&cliaddr, len); /* 客户端也需要bind, 不能依赖自动绑定*/
bzero(&servaddr, sizeof(servaddr));
//服务器地址结构体成员初始化 用于连接 /* 构造server 地址 */
servaddr.sun_family = AF_UNIX;
strcpy(servaddr.sun_path,SERVER_ADDR);
len = offsetof(struct sockaddr_un, sun_path) + strlen(servaddr.sun_path); /* 计算服务器端地址结构有效长度 */
connect(cfd, (struct sockaddr *)&servaddr, len);
while (fgets(buf, sizeof(buf), stdin) != NULL) {
write(cfd, buf, strlen(buf));
len = read(cfd, buf, sizeof(buf));
write(STDOUT_FILENO, buf, len);
}
close(cfd);
return 0;
}
运行结果
服务端:
客户端: