[置顶] 无名管道的C++封装

时间:2022-10-30 04:35:57

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

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

源码在Github上,点击下载

[置顶] 无名管道的C++封装的更多相关文章

  1. &lbrack;知了堂学习笔记&rsqb;&lowbar;css3特效第二篇--行走的线条&amp&semi;&amp&semi;置顶导航栏

    一.行走的线条. 效果图(加载可能会慢一点儿,请稍等...): html代码: <div class="movingLines"> <img src=" ...

  2. &lbrack;置顶&rsqb; Web用户的身份验证及WebApi权限验证流程的设计和实现 &lpar;不是Token驗證&excl;&excl;&excl;不是Token驗證&excl;&excl;&excl;都是基於用户身份的票据信息驗證&excl;&excl;&excl;&rpar;

     转发 http://blog.csdn.net/besley/article/details/8516894 不是Token驗證!!!不是Token驗證!!!都是基於用户身份的票据信息驗證!!! [ ...

  3. &lbrack;置顶&rsqb; Silverlight之控件应用总结(一)(3)

    [置顶] Silverlight之控件应用总结(一)(3) 分类: 技术2012-04-02 20:35 2442人阅读 评论(1) 收藏 举报 silverlightradiobuttondatat ...

  4. css3特效第二篇--行走的线条&amp&semi;&amp&semi;置顶导航栏

    一.行走的线条. 效果图(加载可能会慢一点儿,请稍等...): html代码: <div class="movingLines"> <img src=" ...

  5. 在UWP中页面滑动导航栏置顶

    最近在研究掌上英雄联盟,主要是用来给自己看新闻,顺便copy个界面改一下段位装装逼,可是在我copy的时候发现这个东西 当你滑动到一定距离的时候导航栏会置顶不动,这个特性在微博和淘宝都有,我看了@ms ...

  6. WinFrom窗体始终置顶

    调用WindowsAPI使窗体始终保持置顶效果,不被其他窗体遮盖: [DllImport("user32.dll", CharSet = CharSet.Auto)] privat ...

  7. winform窗体置顶

    winform窗体置顶 金刚 winform 置顶 今天做了一个winform小工具.需要设置置顶功能. 网上找了下,发现百度真的很垃圾... 还是必应靠谱些. 找到一个可以链接. https://s ...

  8. 自定义置顶TOP按钮

    简述一下,分为三个步骤: 1. 添加Html代码 2. 调整Css样式 3. 添加Jquery代码 具体代码如下: <style type="text/css"> #G ...

  9. ahk之路:利用ahk在window7下实现窗口置顶

    操作系统:win7 64位 ahk版本:autohotkey_L1.1.24.03 今天安装了AutoHotkey_1.1.24.03.SciTE.PuloversMacroCreator,重新开始我 ...

随机推荐

  1. jsp页面添加一个集合数组到action(用序列化提交)

    页面的js //点击a标签增加删除 var i=0; $("#a").on("click",function(){ var $newtr = $("& ...

  2. CentOS6 root 用户 vi&sol;vim 无法开启高亮

    编辑 /etc/profile.d/vim.sh if [ -n "$BASH_VERSION" -o -n "$KSH_VERSION" -o -n &quo ...

  3. Matrix&lpar;线段树版&rpar;

    poj2155:http://poj.org/problem?id=2155 题意;同上一遍随笔. 题解:这里用二维线段树打了一发.第一次学习别人的代码.才学的.这种树套树的程序,确实很费脑子,一不小 ...

  4. hdu 3631 Shortest Path

    floyd算法好像很奇妙的样子.可以做到每次加入一个点再以这个点为中间点去更新最短路,效率是n*n. #include<cstdio> #include<cstring> #i ...

  5. Actifio OnVault 8&period;0

  6. &lbrack;AMPPZ2014&rsqb;Jaskinia

    [AMPPZ2014]Jaskinia 题目大意: 一个\(n(n\le3\times10^5)\)的树,\(m(m\le3\times10^5)\)个约束条件\((a_i,b_i,d_i)\).请你 ...

  7. Python参数传递(传值&amp&semi;传引用)

    # 测试参数是传值还是传引用def test(arg): print("test before") print(id(arg)) arg[1]=30 # 测试可变对象 # arg[ ...

  8. SkylineDemoForWeb JavaScript二次开发示例代码

    SkylineDemoForWeb JavaScript二次开发示例代码 http://files.cnblogs.com/files/yitianhe/SkylineDemoForWeb.zip

  9. linux常用命令:rmdir 命令

    今天学习一下linux中命令: rmdir命令.rmdir是常用的命令,该命令的功能是删除空目录,一个目录被删除之前必须是空的.(注意,rm - r dir命令可代替rmdir,但是有很大危险性.)删 ...

  10. Junit 并行执行测试

    从Junit4.7开始可以并行运行测试. 必须设置parallel 参数,可以改变threadCount或useUnlimitedThreads属性. 测试中指定了parallel,项目使用的是 JU ...