ipc 通信介绍

ipc 通信介绍

linux应用开发中,进程中通信的使用是不可能避免的,本文介绍常用的进程间通信方式。有名管道,消息队列,域套接字。

一,有名管道

管道文件,可以由任意进程访问,打开管道就可以指定读写方式
通过文件IO操作,内容在内存中,读写端全部关闭就释放,使用时注意,读端打开是会阻塞,写端打开时才运行,写端存在但是不写数据,读端也会阻塞,可以使用open read write进行读写,一但读到文件末尾,需要关闭,否则一直读到“”,返回为0。

代码实现:
#include<stdio.h>
#include<stdlib.h>
#include<sys/stat.h>
#include<fcntl.h>



int main(int argc, const char *argv[])
{
	int fd;
 	char ch;

	mknod("/home/colin/test/fifo_test",S_IFIFO|0666,0);

	fd = open("/home/colin/test/fifo_test",O_RDONLY);

	printf("open fifo is success \n");
	while(1)
	{
		if(!read(fd,&ch,1))
		{
			sleep(1);
			close(fd);   //这里注意,如果不关闭,一旦另外写管道了 这边就会一直从管道读,即使读不到也不会停下来
			fd = open("/home/test/fifo_test",O_RDONLY);
			printf("reopen fifo \n");
			continue;
		}
		else
		{
			printf("read fifo is %x\n",ch);
		}
	
	}


	return 0;
}
实测

运行程序,产生管道文件,并且阻塞到open之后 读之前
在这里插入图片描述
另外一个进程写管道时,读进程才会取消阻塞
在这里插入图片描述
这里注意代码中的描述:
一旦管道 阻塞到读到数据后,如果读到文件末尾不关闭 管道,程序就回一直从管道中读数据,如果没有数据就返回0。

二. 消息队列

1.消息队列是消息的链表,具有特定的格式,存放在内存中并由消息队列标识符标识.
2.消息队列允许一个或多个进程向它写入与读取消息.
3.管道和命名管道都是通信数据都是先进先出的原则。
4.消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取.比FIFO更有优势。
目前主要有两种类型的消息队列:POSIX消息队列以及系统V消息队列,系统V消息队列目前被大量使用。系统V消息队列是随内核持续的,只有在内核重起或者人工删除时,该消息队列才会被删除。

代码实现,这里不写测试程序了
//初始化两个消息队列
int msg_queue_init(void)
{
	key_t key_s;
	int ret = 0;

	if ((key_s = ftok("/etc", 'm')) == -1) // send queue
	{
		log_dbg("fail to ftok");
		ret = -1;
	}

	if((msgid1 = msgget(key_s, IPC_EXCL)) < 0)
	{
		if((msgid1 = msgget(key_s,0666 | IPC_CREAT)) < 0)
		{
			log_dbg("msget1 error");
			ret = -1;
		}
	}
	else
	{
		log_dbg("msgid exist, msgid = %d\n", msgid1);
	}

	return ret;
}

//定义消息类型
struct msgbuf{
      int mtype;
      char buf[100];
};

//调用接口发送与接收
发送接口
 msgsnd(msgid1, msgp, msgsz, msgflg);
接收接口
 msgrcv(msgid1, msgp, msgsz, msgtype, msgflg); 

三.域套接字

UNIX域套接字(UDS):UNIX Domain Socket
UNIX域socket实现本地进程间通信,需要一个进程作为服务器端,一个进程作为客户端,使用方法有点像socket网络,但又不经过网络底层的那些东西。与网络编程最不一样的地方是服务器端bind的时候用的是sockaddr_un结构,客户端connect的时候用的也是sockaddr_un结构,而不是sockaddr_in或sockaddr。而对于sockaddr_un结构,重点是给它提供一个bind()函数生成的socket类型文件的路径,即sockaddr_un.sun_path的值。并且客户端与服务器端的这个sockaddr_un结构的sun_path是一致的,通常这个路径是众所众知的,就像百度的域名那样。
经过bind,listen,accept,和connect后,两进程就通过读写socket文件描述符来通信,具体是服务器端读写accept返回的socket文件描述符,客户端读写经过connect处理后的文件描述符。

代码实现

service代码

#include<stdio.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<unistd.h>
#include<string.h>
#include<sys/un.h>

#define dbg_printf(...) printf("[%s.%d]",__func__,__LINE__);printf(__VA_ARGS__)
#define UNIX_SOCKET_FILE  "/home/test/temp.file"


static int sockfd = 0;
static struct sockaddr_un servaddr,cliaddr;


int main(int argc, const char *argv[])
{
	
	int ret = 0;
	int connfd = 0;

	sockfd = socket(AF_UNIX,SOCK_STREAM,0);
	if(sockfd < 0 )
	{
		dbg_printf("socket error \n");
		return -1;
	}

	if(access(UNIX_SOCKET_FILE,F_OK) == 0)
	{
		dbg_printf("remove unix socket file \n");
		remove(UNIX_SOCKET_FILE);
	}

	memset(&servaddr,0,sizeof(servaddr));
	servaddr.sun_family = AF_UNIX;
	strcpy(servaddr.sun_path,UNIX_SOCKET_FILE);

	if(0 != bind(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr)))
	{
		dbg_printf("bind fail \n");
		close(sockfd);
		return -1;
	}

	if(0 != listen(sockfd,10))
	{
		dbg_printf("linsten fail \n");
		close(sockfd);
		return -1;
	}


	int clilen = sizeof(cliaddr.sun_family)+strlen(cliaddr.sun_path);

	if((connfd = accept(sockfd,(struct sockaddr *)&cliaddr,(socklen_t *)&clilen)) == -1)
	{
		dbg_printf("accept fail \n");
		return -1;
	}

	dbg_printf("accept success connfd = %d \n",connfd);

	char recvbuf[64];
	int n = 0;

	while(1)
	{
		memset(recvbuf,0,sizeof(recvbuf));

		n = recv(connfd,recvbuf,sizeof(recvbuf),0);
		if(n < 0)
		{
			dbg_printf("recv msg fail n= %d \n",n);
			return -1;
		}

		dbg_printf("recv is %s \n",recvbuf);

		send(connfd,recvbuf,n,0);
	
	}

	close(sockfd);
	close(connfd);

	return 0;
}

client代码

#include<stdio.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<unistd.h>
#include<string.h>
#include<sys/un.h>

#define dbg_printf(...) printf("[%s.%d]",__func__,__LINE__);printf(__VA_ARGS__)
#define UNIX_SOCKET_FILE  "/home/test/temp.file"


static int sockfd = 0;
static struct sockaddr_un servaddr,cliaddr;


int main(int argc, const char *argv[])
{
	
	int ret = 0;
	int connfd = 0;

	sockfd = socket(AF_UNIX,SOCK_STREAM,0);
	if(sockfd < 0 )
	{
		dbg_printf("socket error \n");
		return -1;
	}

	memset(&servaddr,0,sizeof(servaddr));
	servaddr.sun_family = AF_UNIX;
	strcpy(servaddr.sun_path,UNIX_SOCKET_FILE);

	
	int serlen = sizeof(servaddr.sun_family)+strlen(servaddr.sun_path);
	if(connect(sockfd,(struct sockaddr *)&servaddr,serlen) == -1)
	{
		dbg_printf("connect fail \n");
		close(sockfd);
		return -1;
	}


	char recvbuf[64];
	char sendbuf[64];

	while(1)
	{
		memset(recvbuf,0,sizeof(recvbuf));
		memset(sendbuf,0,sizeof(sendbuf));

		strcpy(sendbuf,"hello world");

		send(sockfd,sendbuf,strlen(sendbuf),0);

		recv(sockfd,recvbuf,sizeof(recvbuf),0);

		dbg_printf("recv is %s \n",recvbuf);
	
	}

	close(sockfd);

	return 0;
}

程序测试

在这里插入图片描述

关于域套接字,还可以加上select机制,或者epoll机制,后面我抽时间实现后 再做记录。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值