使用UNIX域套接字传递pipe管道fd进行跨进程读写的一个例子

7 篇文章 0 订阅

我们知道可以使用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)

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是一个简单的 C++ 程序,用于在 UNIX 进行套接字通信: ```cpp #include <iostream> #include <sys/socket.h> #include <sys/un.h> #include <unistd.h> #include <cstring> const char* SOCKET_PATH = "/tmp/mysocket"; int main() { // 创建套接字 int sockfd = socket(AF_UNIX, SOCK_STREAM, 0); if (sockfd == -1) { std::cerr << "Failed to create socket" << std::endl; return 1; } // 绑定到 socket 地址 struct sockaddr_un addr; memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; strncpy(addr.sun_path, SOCKET_PATH, sizeof(addr.sun_path) - 1); if (bind(sockfd, (struct sockaddr*)&addr, sizeof(addr)) == -1) { std::cerr << "Failed to bind socket" << std::endl; close(sockfd); return 1; } // 监听连接请求 if (listen(sockfd, 5) == -1) { std::cerr << "Failed to listen on socket" << std::endl; close(sockfd); return 1; } // 等待客户端连接 int clientfd = accept(sockfd, nullptr, nullptr); if (clientfd == -1) { std::cerr << "Failed to accept client" << std::endl; close(sockfd); return 1; } // 读取客户端发送的数据 char buf[256]; int numBytes = read(clientfd, buf, sizeof(buf)); if (numBytes == -1) { std::cerr << "Failed to read from client" << std::endl; close(clientfd); close(sockfd); return 1; } // 打印接收到的数据 std::cout << "Received " << numBytes << " bytes: " << buf << std::endl; // 关闭套接字 close(clientfd); close(sockfd); return 0; } ``` 在这个程序中,我们首先使用 `socket` 函数创建了一个 UNIX 套接字。然后,我们使用 `bind` 函数将套接字绑定到一个本地地址上,这里我们使用了 `/tmp/mysocket` 作为地址。接下来,我们使用 `listen` 函数开始监听连接请求。当有客户端连接时,使用 `accept` 函数接受连接请求,并返回一个新的套接字,可以使用这个套接字与客户端进行通信。在这个程序中,我们只是简单地从客户端读取数据,并将其打印到标准输出流中。最后,我们关闭套接字并退出程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值