我们知道可以使用UNIX域套接字在同一台主机上的进程间传递文件描述符。可以利用这个特性来做进程间通信。当然内核会自动给fd做好映射,比如一个文件f在进程A的fd为5,当用UNIX域套接字把fd传递到进程B之后,在进程B中fd进过内核映射之后可能变成其它的值。
在研究Tizen系统的log模块时发现了一种使用管道作为进程通信的方法,就是通过UNIX域套接字传递pipe管道fd给其它进程来通信。而且两个进程是独立的没有继承关系的进程。在Unix环境高级编程这本书中关于管道的描述:通常,一个管道由一个进程创建,在进程调用fork之后,这个管道就能在父进程和子进程之间使用了。本文提供一种在没有继承关系的进程通过管道通信的一种方法。
交互如下:
server创建一个pipe之后通过socket将pipe的fd文件描述符发送给client;client收到pipe fd之后往pipe写数据,server收数据。可以证明pipe在非父子进程,没有继承关系的进程间也可以进行通信。
server:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<errno.h>
#include<sys/un.h>
#define SOCK_FILE "./chenxh" //通信的socket文件
int create_local_sock(char *sockfile)//创建socket
{
int local_fd = 0;
struct sockaddr_un serveraddr;
if(-1 == (local_fd=socket(AF_LOCAL,SOCK_STREAM,0)))//TCP方式
{
perror("socket");
exit(EXIT_FAILURE);
}
bzero(&serveraddr,sizeof(serveraddr));
serveraddr.sun_family = AF_LOCAL;
strncpy(serveraddr.sun_path,sockfile,strlen(sockfile));
if(-1 == connect(local_fd,(struct sockaddr *)&serveraddr,sizeof(serveraddr)))
{
perror("connect");
exit(EXIT_FAILURE);
}
return local_fd;
}
static int recv_fd(int sock_fd,int *fd_to_recv,char *buf,int len)//接收消息
{
struct cmsghdr *cmsg;
cmsg = alloca(sizeof(struct cmsghdr) + sizeof(int));//分配控制消息存储空间
cmsg->cmsg_len = sizeof(struct cmsghdr) + sizeof(int);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
struct msghdr msg;
msg.msg_control = cmsg;
//cmsg=CMSG_FIRSTHDR(&msg);
msg.msg_controllen = cmsg->cmsg_len;
msg.msg_name = NULL;
msg.msg_namelen = 0;
struct iovec iov[3]; //普通消息接收空间
iov[0].iov_base = buf;
iov[0].iov_len = len;
msg.msg_iov = iov;
msg.msg_iovlen = 3;
if(recvmsg(sock_fd,&msg,0) < 0) //接收消息,从服务器端获得的msg结构体的信息保存在recvmsg()函数的第二个参数msg中。
{
printf("recvmsg error,errno is %d\n",errno);
fprintf(stderr,"recvmsg failed.errno:%s\n",strerror(errno));
return errno;
}
memcpy(fd_to_recv, CMSG_DATA(cmsg), sizeof(int)); //获取文件描述符
//*fd_to_recv+=1;
printf("fd_to_recv: %d \n", *fd_to_recv);
if(msg.msg_controllen != cmsg->cmsg_len)
{
*fd_to_recv = -1;
}
return 0;
}
int main(int argc,char *argv[])
{
int sock_fd = 0;
int file_fd;
char *ptr = "now write data to file \n" ; //写入到文件的内容
char buf[1024];
memset(buf,'\0',1024);
sock_fd = create_local_sock(SOCK_FILE); //创建socket
recv_fd(sock_fd,&file_fd,buf,1023); //接收消息
printf("recv message:%s\n",buf); //接收到的消息,这个普通消息为什么全部保存在buf?
memset(buf,'\0',128);
printf("this is in the child process, here read s \n");
char s[] = "Hello world, xxxxxthis is write pipe\n";
write(file_fd, s , sizeof(s));
//close(file_fd);
unlink(SOCK_FILE);
return 0;
}
client:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<errno.h>
#include<fcntl.h>
#include<sys/un.h>
#define SOCK_FILE "./chenxh" //本地通信时Socket文件的文件名
int create_local_sock(char *sockfile)//次函数用于创建本地Socket
{
int local_fd;
struct sockaddr_un myaddr;
if(-1 == (local_fd = socket(AF_LOCAL,SOCK_STREAM,0)))//创建Socket
{
perror("socket");
exit(EXIT_FAILURE);
}
bzero(&myaddr,sizeof(myaddr));
myaddr.sun_family = AF_LOCAL;//本地Socket
strncpy(myaddr.sun_path,sockfile,strlen(sockfile));
if(-1 == bind(local_fd,(struct sockaddr *)&myaddr,sizeof(myaddr)))//绑定
{
perror("bind");
exit(EXIT_FAILURE);
}
if(-1 == listen(local_fd,5))//监听
{
perror("listen");
exit(EXIT_FAILURE);
}
int new_fd;
struct sockaddr_un peeraddr;
int len = sizeof(peeraddr);
new_fd = accept(local_fd,(struct sockaddr *)&peeraddr,&len);//阻塞等待
if (-1 == new_fd)
{
perror("accept");
exit(EXIT_FAILURE);
}
return new_fd;
}
int send_fd(int sock_fd,char *file) //创建pipe,并通过socket发送文件描述符,期望对方写此文件描述符
{
int fd_to_send;
pipe(fds);
fd_to_send=fds[1];
//fd_to_send=fds[1];
fcntl(fds[1], 100, 20);
printf("fd_to_send: %d \n", fd_to_send);
struct cmsghdr *cmsg;//描述符控制信息
cmsg = alloca(sizeof(struct cmsghdr) + sizeof(fd_to_send));//申请控制空间
cmsg->cmsg_len = sizeof(struct cmsghdr) + sizeof(fd_to_send);//控制消息长度
cmsg->cmsg_level = SOL_SOCKET;//控制消息级别
cmsg->cmsg_type = SCM_RIGHTS;//控制消息类型
memcpy(CMSG_DATA(cmsg),&fd_to_send,sizeof(fd_to_send));//控制消息内容
struct msghdr msg; //整个消息
msg.msg_control = cmsg;//初始化控制消息
//cmsg=CMSG_FIRSTHDR(&msg);
msg.msg_controllen = cmsg->cmsg_len;
msg.msg_name = NULL;
msg.msg_namelen = 0;
struct iovec iov[3]; //普通消息空间
iov[0].iov_base = "hello,"; //普通消息1
iov[0].iov_len = strlen("hello,");
iov[1].iov_base = "this is chenxh,";//普通消息2
iov[1].iov_len = strlen("this is chenxh,");
iov[2].iov_base = "and you?";//普通消息3
iov[2].iov_len = strlen("and you?");
msg.msg_iov = iov;
msg.msg_iovlen = 3;
if(sendmsg(sock_fd,&msg,0) < 0) //将消息一起发送给对方
{
printf("sendmsg error,errno is %d\n",errno);
fprintf(stderr,"sendmsg failed.errno:%s\n",strerror(errno));
return errno;
}
char buf[129];
memset(buf,'\0',128);
read(fds[0], buf, sizeof(buf)); //等待对方往管道写数据
printf("received buf : %s \n", buf);
close(fd_to_send);
return 1;
}
#define TESTF "testfd.txt"
int main(int argc,char *argv[])
{
int sock_fd = 0;
unlink(SOCK_FILE);
#if 0
if(argc != 2) //argv[1]为期望对方内容的文件
{
printf("pls usage %s file_send\n",argv[0]);
exit(EXIT_FAILURE);
}
#endif
sock_fd = create_local_sock(SOCK_FILE); //创建socket
if(send_fd(sock_fd, TESTF) != 1)
{
printf("send error");
exit(EXIT_FAILURE);
}
return 0;
}
makefile:
CC=gcc
SRC := $(wildcard *.c)
OBJ := $(SRC:.c=.o)
all:send_fd_client send_fd_server
send_fd_client:send_fd_client.o
$(CC) $^ $(CFLAGS) -fPIC $(LIBS) -o $@
send_fd_server:send_fd_server.o
$(CC) $^ $(CFLAGS) -fPIC $(LIBS) -o $@
%.o: %.c %.h
$(CC) -c -o $@ $< $(CFLAGS) -fPIC $(LIBS)
clean:
rm -rf $(OBJ)