[置顶] 无名管道的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
#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++封装的更多相关文章
- [知了堂学习笔记]_css3特效第二篇--行走的线条&&置顶导航栏
一.行走的线条. 效果图(加载可能会慢一点儿,请稍等...): html代码: <div class="movingLines"> <img src=" ...
- [置顶] Web用户的身份验证及WebApi权限验证流程的设计和实现 (不是Token驗證!!!不是Token驗證!!!都是基於用户身份的票据信息驗證!!!)
转发 http://blog.csdn.net/besley/article/details/8516894 不是Token驗證!!!不是Token驗證!!!都是基於用户身份的票据信息驗證!!! [ ...
- [置顶] Silverlight之控件应用总结(一)(3)
[置顶] Silverlight之控件应用总结(一)(3) 分类: 技术2012-04-02 20:35 2442人阅读 评论(1) 收藏 举报 silverlightradiobuttondatat ...
- css3特效第二篇--行走的线条&&置顶导航栏
一.行走的线条. 效果图(加载可能会慢一点儿,请稍等...): html代码: <div class="movingLines"> <img src=" ...
- 在UWP中页面滑动导航栏置顶
最近在研究掌上英雄联盟,主要是用来给自己看新闻,顺便copy个界面改一下段位装装逼,可是在我copy的时候发现这个东西 当你滑动到一定距离的时候导航栏会置顶不动,这个特性在微博和淘宝都有,我看了@ms ...
- WinFrom窗体始终置顶
调用WindowsAPI使窗体始终保持置顶效果,不被其他窗体遮盖: [DllImport("user32.dll", CharSet = CharSet.Auto)] privat ...
- winform窗体置顶
winform窗体置顶 金刚 winform 置顶 今天做了一个winform小工具.需要设置置顶功能. 网上找了下,发现百度真的很垃圾... 还是必应靠谱些. 找到一个可以链接. https://s ...
- 自定义置顶TOP按钮
简述一下,分为三个步骤: 1. 添加Html代码 2. 调整Css样式 3. 添加Jquery代码 具体代码如下: <style type="text/css"> #G ...
- ahk之路:利用ahk在window7下实现窗口置顶
操作系统:win7 64位 ahk版本:autohotkey_L1.1.24.03 今天安装了AutoHotkey_1.1.24.03.SciTE.PuloversMacroCreator,重新开始我 ...
随机推荐
- JS,Jquery - 三元运算符
在javascript中使用三元运算符. 要使用 " [] " ,对运算式进行包裹.
- CentOS安装rar及用法
1.下载安装rar wget http://www.rarsoft.com/rar/rarlinux-x64-5.4.b3.tar.gztar -zxvf rarlinux-x64-.tar.gz - ...
- 【转】LINUX下一款不错的网站压力测试工具webbench
原文链接:http://blog.csdn.net/xinqingch/article/details/8618704 安装: wget http://blog.s135.com/soft/linux ...
- Python监控网站运行状况
利用python便捷的类库,可以方便快速实现对网站运行状况的监控,主要包括对80端口(即网站运行端口),其它tcp服务等端口的监控就可以了解服务器大概的一个运行状况,使用的库主要为urllib2及so ...
- MVC-03 控制器(4)
七.模型绑定 在ASP.NET MVC中是通过模型绑定(Model Binding)达到解析客户端传来的数据. 1.简单模型绑定 当网页上有个窗体,且窗体内有个名为Username的输入字段,而Act ...
- 基于Sql Server 2008的分布式数据库的实践(二)
原文 基于Sql Server 2008的分布式数据库的实践(二) 从Win7连接Win2003的Sql Server 2008 1.新建链接服务器链接到Win2003的Sql Server 2008 ...
- asp.net jquery+ajax异步刷新1
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- matlab绘图方法汇总
Matlab画图 强大的画图功能是Matlab的特点之中的一个,Matlab提供了一系列的画图函数,用户不须要过多的考虑画图的细节,仅仅须要给出一些基本參数就能得到所需图形,这类函数称为高层画图函数. ...
- eclipse、MyEclipse实现批量改动文件编码
在使用eclipse或MyEclipse编程时,常常遇到部分文件打开后出现乱码的情况(特别是在导入项目后) 1:右击项目选择properties->Resource>Other选择UTF- ...
- c语言中scanf()、printf()函数
函数调用scanf(“%d”, &weight) 包含两个参数:“%d” 和&weight.C用逗号来隔开函数调用中的多个参数: 但是printf()和scanf()函数比较特殊,其 ...