无名管道的C++封装

xpipe-无名管道的C++封装类

无名管道的C++封装类,用于父子进程进行通信

基础介绍


unix下一切皆文件,管道也不例外。无名管道pipe定义在<unistd.h>中。

 #include <unistd.h>
 int pipe(int fd[2]);

其中fd[0]是读端,fd[1]是写端,fd[1]的输出是fd[0]的输入,因此管道是一个有向的半双工通信方式。使用`write(fd[1],...)`和`read(fd[0],...)`对管道中的信息进行读写。无名管道通常运用于父子进程间通信。关闭读端或者写端是使用`close`函数,同文件句柄一样,关闭后不能重新打开。如果关闭后使用该端,系统会发送一个`SIGPIPE`的信号。作为一个文件,管道有一个缓存大小限制,这是一个运行时限制,使用`fpathconf`函数可以查看其大小,类型名为`_PC_PIPE_BUF`.
如:

 cout<<fpathconf(fd[0],_PC_PIPE_BUF)<<endl;

在我的 Ubuntu10.10 下为4096字节,刚好一页大小。而在AIX服务器上,管道大小的限制则为32768字节。

读写管道使用系统函数read和write,如:

 write(m_fd[1],content.c_str(),content.length());

这能体现管道作为文件的本质,但不能体现通信的意图,因此我将管道的读写封装为与socket中发送和接收。
 

ssize_t xpipe::send(void *buf, size_t n)
 {
  return write(m_fd[1], buf, n);
 }
 ssize_t xpipe::recv(void *buf, size_t nbytes)
 {
  return read(m_fd[0], buf, nbytes);
 }

使用中,通信的内容常常为字符串,上述两个函数不仅能满足这个要求,还能传递一些简单结构体消息(稍后在讨论),但是每次都要输入长度。为简化开发,我将send和recv重载,作为特化方法,方便字符串的传递。使用方法非常简单,如:

 xpipe x;
 x.send("Whose your daddy?");
 string rs;
 x.recv(rs);

关于简单结构体,需要作个说明,这里指的是由C++基本类型组合而成的结构体,如:

 class child
 {
 public:
  long id;
  char name[20];
 };

注意: string不是基本类型 。传递结构体消息示例如下:

 xpipe x;
 child cc;
 cc.id=10;
 strcpy(cc.name,"PAYBY");
 x.send((child *)&cc,sizeof(child));
 /*-------------------------*/
 child dd;
 x.recv((child *)&dd,sizeof(child)); 

通信设计


文件是常见的通信媒介。但对文件的读写必须要加上读写的角色信息才能体现通信的过程。一个简单的单向通信包含消息发送方和消息接收方。对管道读写时常常有可以看到接收进程(读进程)关闭写端口,发送进程(写进程)关闭读端口,这样做是为了确保信息流向的单一,以免信息从接收进程流向发送进程。对通信而言,这依然不够直观。单向的信息流动中,一方仅仅是发送者(senderonly),另一方仅仅是接收者(receiveronly)。因此,在父子进程通信过程中,给他们指定角色就可以了。
示例代码如下:
 xpipe x;
 pid_t pid=fork();
 string item="whose your daddy";
 if (pid==0)
 {//child process
  x.receiveronly();

  string rs;
  x.recv(rs);
  //check point
  assert(rs==item);
  exit(0);
 }
 else if (pid>0)
 {//parent process
  int ret;
  x.senderonly();
  x.send(item);
  wait(&ret);
 }

在示例代码中父进程被指定为发送者(x.senderonly();),不能通过`x`管道进行接收信息,子进程被指定为接收者(x.receiveronly();),不能通过x管道进行发送信息。要实现父子进程的互相通信。可以在指定另一个管道,将子进程指定为发送者,父进程指定为接收者。(见使用示例)

使用示例


父子进程互相通信

 

xpipe x;
 xpipe y;
 pid_t pid=fork();
 string x_item="whose your daddy?";
 string y_item="my father is Ligang!";
 if (pid==0)
 {//child process
  x.receiveronly();
  y.senderonly();
  string rs;
  x.recv(rs);
  //check point
  assert(rs==x_item);

  y.send(y_item);
  cout<<"child process:"<<y_item<<endl;
  exit(0);
 }
 else if (pid>0)
 {//parent process
  int ret;
  x.senderonly();
  y.receiveronly();
  x.send(x_item);
  cout<<"parent process:"<<x_item<<endl;

  string ts;
  y.recv(ts);
  assert(ts==y_item);
  wait(&ret);
 }

预期结果为:

parent process:whose your daddy?
child process:my father is Ligang!

代码一览



头文件xpipe.h

#ifndef __XPIPEH__
#define __XPIPEH__

#include <unistd.h>
#include <string>
#include <string.h>
#include <stdio.h>
using namespace std;
/*
	无名管道的C++封装类,用于父子进程进行通信
	时间 :2013年7月15日 20:30:58
	邮 箱:chen_xueyou@163.com
*/
class xpipe
{
public:
	xpipe();
	~xpipe();
///核心方法
	ssize_t send(void *buf, size_t n);
	ssize_t recv(void *buf, size_t nbytes);
///常用方法特化
	void 	send(const string &content);
	void 	recv(string &content);
//确定通信角色
	void	senderonly(){DisReadable();}
	void	receiveronly(){DisWriteable();}

//属性操作
	string	role() const;

	long 	Bufsize(long newbufsize=0);
private:
	//读写关闭操作
	void 	DisReadable();
	void 	DisWriteable();

	/* data */
private:
	int 		m_fd[2];
	bool 	m_readable;
	bool 	m_writeable;

	long 	m_bufsize;
	char *	m_buf;
};
#endif

xpipe.cpp

#ifndef __XPIPECPP__
#define __XPIPECPP__

#include "xpipe.h"

xpipe::xpipe()
:m_readable(true),m_writeable(true),m_buf(NULL)
{
	int success=pipe(m_fd);
	if(success<0)
	{
		throw puts("create pipe failed!");
	}
	//检测系统设置的管道限制大小
	m_bufsize=fpathconf(m_fd[0],_PC_PIPE_BUF);

}
xpipe::~xpipe()
{
	if(m_readable)
		close(m_fd[0]);
	if(m_writeable)
		close(m_fd[1]);
	if(m_buf!=NULL)
		delete m_buf;
}
ssize_t xpipe::send(void *buf, size_t n)
{
	return write(m_fd[1], buf, n);
}
ssize_t xpipe::recv(void *buf, size_t nbytes)
{
	return read(m_fd[0], buf, nbytes);
}
void xpipe::send(const string &content)
{
	write(m_fd[1],content.c_str(),content.length());
}
void  xpipe::recv(string &content)
{
	if (m_buf==NULL)
	{//lazy run
		m_buf=new char[m_bufsize];
		if (m_buf==NULL)
		{
			throw puts("memory not enough!");
		}
	}
	memset(m_buf,0,m_bufsize);
	read(m_fd[0],m_buf,m_bufsize);
	content=string(m_buf);
}
//返回当前管道所扮演到角色
string xpipe::role() const
{
	if (m_writeable&&m_readable)
	{
		return "sender and receiver";
	}
	if (m_writeable)
	{
		return "sender";
	}
	if (m_readable)
	{
		return "receiver";
	}

	return "none";

}
/*关闭读端口*/
void xpipe::DisReadable()
{
	if(m_readable)
	{
		close(m_fd[0]);
		m_readable=false;
	}
}
/*关闭写端口*/
void xpipe::DisWriteable()
{
	if (m_writeable)
	{
		close(m_fd[1]);
		m_writeable=false;
	}

}
/*如果输入大于0:调整缓存区大小,并返回调整后缓存区大小
  如果输入小于等于0,则不设置,只返回缓存区大小
  缓存区大小构造时默认设置为系统对管道的限制大小
  默认参数为0
 */
long xpipe::Bufsize(long newbufsize)
{
	//大于0才设置
	if (newbufsize>0)
	{
		m_bufsize=newbufsize;
		delete m_buf;
		//重新申请缓存区
		m_buf=new char[m_bufsize];
		if (m_buf==NULL)
		{
			throw puts("memory not enough!");
		}
	}

	return m_bufsize;
}
#endif

测试文件test.cpp

#include <iostream>
#include <assert.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include "xpipe.h"
using namespace std;

/*test Bufszie*/
void test1()
{
	xpipe x;
	int fd[2];
	pipe(fd);
	//check point
	assert(x.Bufsize()==fpathconf(fd[0],_PC_PIPE_BUF));
	x.Bufsize(20);
	//check point
	assert(x.Bufsize()==20);
}

/*test read/recv*/
/////////////////////////////////////
class childreq
{
public:
	long recid;
	char billtype[20];

};
void test2()
{
	xpipe x;
	pid_t pid=fork();
	if (pid==0)
	{
		x.receiveronly();

		childreq dd;
		x.recv((childreq *)&dd,sizeof(childreq));
		//check point
		assert(dd.recid==10);
		assert(!strcmp(dd.billtype,"PAYBY"));
		exit(0);
	}
	else if (pid>0)
	{
		x.senderonly();

		childreq cc;
		cc.recid=10;
		strcpy(cc.billtype,"PAYBY");

		x.send((childreq *)&cc,sizeof(childreq));
		int ret;
		wait(&ret);
	}
}

/*test read/recv*/
void test3()
{
	xpipe x;
	pid_t pid=fork();
	string item="whose your daddy";
	if (pid==0)
	{//child process
		x.receiveronly();

		string rs;
		x.recv(rs);
		//check point
		assert(rs==item);
		exit(0);
	}
	else if (pid>0)
	{//parent process
		int ret;
		x.senderonly();
		x.send(item);
		wait(&ret);
	}
}
/*test role*/
void test4()
{
	xpipe x;
	assert(x.role()=="sender and receiver");
	x.senderonly();
	assert(x.role()=="sender");
	x.receiveronly();
	assert(x.role()=="none");

	xpipe y;
	y.receiveronly();
	assert(y.role()=="receiver");

}
/*test read/recv*/
void test5()
{
	xpipe x;
	xpipe y;
	pid_t pid=fork();
	string x_item="whose your daddy?";
	string y_item="my father is Ligang!";
	if (pid==0)
	{//child process
		x.receiveronly();
		y.senderonly();

		string rs;
		x.recv(rs);
		//check point
		assert(rs==x_item);

		y.send(y_item);
		cout<<"child process:"<<y_item<<endl;
		exit(0);
	}
	else if (pid>0)
	{//parent process
		int ret;
		x.senderonly();
		y.receiveronly();

		x.send(x_item);
		cout<<"parent process:"<<x_item<<endl;

		string ts;
		y.recv(ts);
		assert(ts==y_item);

		wait(&ret);
	}
}
int main(int argc, char const *argv[])
{
	test1();
	test2();
	test3();
	test4();
	test5();
	cout<<"pass all the tests"<<endl;
}

makefile文件

CXX=g++
all:
        $(CXX) -c xpipe.cpp
        $(CXX) test.cpp -o test xpipe.o
clean:
        rm xpipe.o test

有问题,请给我留言,我的0CSDN博客 |新浪微博 |  个人网站

源码在Github上,点击下载

时间: 2024-11-09 06:39:35

无名管道的C++封装的相关文章

优化PHP程序

 网上很多文章介绍优化php程序,是通过安装Zend Optimizer之类的加速软件实现的,但这种加速是有限的.本文主要从程序代码着手介绍一些优化手段. 1.程序的抽象层越多,各抽象层分离得越严格,程序效率越低. 最原始的应用于网页的php程序模式莫过于脚本嵌入模式,即仅仅在一个网页中需要动态处理或显示数据的地方通过加入标识符嵌入php脚本.一般来说这是php程序员最早学习的模式,它只有一个抽象层,就是网页,故本文称其为单层模式. 随着网站规模逐渐增大,程序员可能会发现单层模式的程序很难维护,

《你必须知道的.net》读书笔记 003 —— 1.3 封装的秘密

    好了继续,封装是一个简单的.基础的,没什么好说的,但是又说不明白的东东.没有源码就是封装起来了,有了源码就不是封装了,因为可以打开来随意修改了.呵呵,开个玩笑先.     字段.属性.     原来对于属性的看法是,如果要用属性而不用public的字段的话,需要两个条件:一是写自定义控件,目的是在属性窗口里可以查看属性,直接赋值:一是在属性里面做判断,比如age属性的set{}里面,可以判断一下,在 0到150才是有效地,超出了就视为无效.如果既不做判断,又不是自定义控件的属性的话,那么

跳出封装剖析ASP.NET脚本回调的原理

asp.net|封装|脚本      [注:此段与标题内容无关,可略过] 在看完两集Stargate并且洗了一个澡之后,我终于决定要开始写这篇文章.这是我第一篇真正意义上的原创技术文章,不管技术含量如何,我总算是迈出了这一步.博客其实开过不少,从最早的校园大巴,到博客园,以及我那个用来发牢骚的新浪博客,其实开博客最初的目的就是用来在写程序的同时,记录一下自己的学习的过程.但是后来演变成了用来发牢骚的东西,这也算是我一直以来不能专心钻研的恶果吧,不过所谓亡羊补牢为时未晚,就从这篇文章开始,变得专注

工厂式封装Windows你也能行

对于那些经常需要给朋友攒机或者重新安装操作系统的电脑高手来说,每次安装Windows系统所经历的漫长等待无异于一次折磨.虽然身边有Ghost之类分区镜像软件,但是每台计算机配置不同造成Windows对于硬件的检测不一样,再加上Windows XP/2003独有的激活策略,这似乎使得Ghost没有了用武之地.其实这些并非没有解决之道,只要将自动应答文件和Ghost有机结合起来,我们也可以打造出一个适用于所有计算机的Windows封装包,这样仅需十分钟即可完成以前几个小时的繁琐安装了. 一.卸载驱动

C#视频监控系列(6):服务器端——封装API(上) [HikServer.dll]

前言 写系列文章的时候[前言]部分变得无言了,可能来得顺利了点吧: ) 本章中提供的封装均是我用笨办法从<<Hikvision 板卡网络开发包编程手册V4.7>>和<<DS-4000HC.HCS.HC+.HF.HS.MD卡的Windows编程指南V4.3>>中拷贝出来并参照 VC++代码进行整理的,主要是针对HikServer.dll和DS40xxSDK.dll的调用封装. 正文 1.HikServer.dll using System; using Sys

十分钟封装万能Windows

对于那些经常需要给朋友攒机或者重新安装操作系统的电脑高手来说,每次安装Windows系统所经历的漫长等待无异于一次折磨.虽然身边有Ghost之类分区镜像软件,但是每台计算机配置不同造成Windows对于硬件的检测不一样,再加上Windows XP/2003独有的激活策略,这似乎使得Ghost没有了用武之地.其实这些并非没有解决之道,只要将自动应答文件和Ghost有机结合起来,我们也可以打造出一个适用于所有计算机的Windows封装包,这样仅需十分钟即可完成以前几个小时的繁琐安装了. 一.卸载驱动

Windows 7封装的自动应答文件制作

  最近在研究Windows 7的封装,虽然网上有一些工具可以实现无人参与封装,但是现在还没有使用过,自己安装了Windows AIK,也在反复的学习,刚开始的时候不知道冲什么地方下手,我有个习惯,不知道的东西,喜欢先上网找现成的实例,然后回来自己研究,反正是先Google,然后在研究,下面就和大家分享一下最近的收获. 首先了,我们来了解一下封装的过程,当你运行了sysprep程序后重启,计算机的配置过程如下 上图用红色线条框起来的,是你使用oobe方式封装后的配置过程,具体哪些设置可以在那个阶

14个你可能不知道的JavaScript调试技巧

以更快的速度和更高的效率来调试JavaScript.熟悉工具可以让工具在工作中发挥出更大的作用.尽管江湖传言 JavaScript 很难调试,但如果你掌握了几个技巧,就能用很少的时间来解决错误和bug.文中已经列出了14个你可能不知道的调试技巧,但是可能需要你牢记在心,以便在下次需要调试JavaScript代码时使用!  1. debugger  除了console.log, debugger是我们最喜欢.快速且肮脏的调试工具.执行代码后,Chrome会在执行时自动停止.你甚至可以把它封装成条件

Java异常封装(自己定义错误码和描述,附源码)

版权声明:本文为博主原创文章,转载注明出处http://blog.csdn.net/u013142781 目录(?)[+] 真正工作了才发现,Java里面的异常在真正工作中使用还是十分普遍的.什么时候该抛出什么异常,这个是必须知道的. 当然真正工作里面主动抛出的异常都是经过分装过的,自己可以定义错误码和异常描述. 下面小宝鸽就为大家介绍一个Java异常简单封装的例子. 在给出异常分装例子之前,需要给大家普及Java里面的checked异常和unchecked异常的个概念. 一.checked异常