目录
管道
pipe创建一个管道
让子进程写入,父进程读取
如何把消息发送/写入给父进程
父进程该怎么读取呢
管道本质
结论:管道的特征:
测试管道大小
写端退了,测试结果
测试子进程一直写,父进程读一会就退出
管道到的应用场景
写一个进程池(pipe_use)
思路步骤
管道创建
创建父子进程
父进程写,子进程读
子进程pid有了管道也有了,就差在父进程添加字段了
先更改一下,在class里构造一下
添加字段
把初始化改造成函数
debug测试函数,纯输入函数
第二步开始控制进程了(想让子进程做什么)
改变一下,直接从键盘(0号描述符)里读,不从管道(3)里读了,就没有管道的概念了,slaver就不用传参了,父进程通过管道写,子进程通过标准输入读
现在开始选择任务和进程
也可以轮询选择,定义一个计数器,++弄,再%等
清理收尾
bug的地方:
编辑
以上是匿名管道
总文件总代码
makefile中代码
Task.hpp中代码
ProcessPool.cc中代码
命名管道
mkfifo命名管道
创建一个共享文件
这里先用server控制管道文件
谁控制的先运行运行谁就好了
命名管道的删除编辑
思路
用到了open
服务端读取数据
打开顺序一定
无法输入空格问题(getline)
会发现一个问题,客户端退出了,服务端还没退出
sever端
写成进程池的样子
日志
用到了可变参数(形参实例化是从右到左)
开始写日志,level日志等级
先定义时间,time,时间戳
ctime
vsnprint
测试,先./server,然后会阻塞,然后./client,就会打印出,logmessage里的信息
为啥./client之前不打印
因为等待写入方式打开后,自己才会打开文件,向后执行,open阻塞了!编辑编辑
往文件里打印(上面是往屏幕打印)
分类
日志放入一个文件测试结果:编辑
编辑
日志分类测试结果:
log.hpp里头文件
优化一下调用
编辑
client.cc
common.hpp
log.hpp
makefile
server.cc
匿名管道
https://blog.csdn.net/2401_83427936/article/details/142603367
首先自己要用用户层缓冲区,还得把用户层缓冲区拷贝到管道里,(从键盘里输入数据到用户层缓冲区里面),然后用户层缓冲区通过系统调用(write)写到管道里,然后再通过read系统调用,被对方(读端)读取,就要从管道拷贝到读端,然后再显示到显示器上。
pipe创建一个管道
pipe的介绍
1完成这件事:
看图分析
运行结果
#include
#include
using namespace std;
int main()
{
//创建管道
//先创建一个pipefd数组
int pipefd[2];
//用n接受一下,判断是否成功
int n = pipe(pipefd);
if(n<0) return 1;//创建失败了
//创建成功
//测试一下文件描述符是3和4
cout<<"pipefd[0]:"< return 0; } 2完成这件事: 创建一个子进程 pid_t id = fork(); if(id < 0)return 2;//创建失败 if(id == 0)//创建成功 { //子进程 } //父进程 让子进程写入,父进程读取 要想让子进程进程写,就需要在进程中关闭读端 if(id == 0)//创建成功 { //子进程 close(pipefd[0]); } 同理 //父进程 close(pipefd[1]); 都用完结束后,可以都关掉 if(id == 0)//创建成功 { //子进程 close(pipefd[0]); //..... close(pipefd[1]); } //父进程 close(pipefd[1]); //..... close(pipefd[0]); IPC code,写通信代码 3这件事也完成了: 结构就有了 然后在pipefd[1]这个管道里写,定义一个Writer函数 if(id == 0)//创建成功 { //子进程 close(pipefd[0]); //.....IPC code,写通信代码 //在pipefd[1]这个管道里写 Writer(pipefd[1]); close(pipefd[1]); exit(0);//正常退出 } 同理父进程的 //父进程 close(pipefd[1]); //.....IPC code,写通信代码 //在pipefd[0]这个管道里写 Reader(pipefd[0]); close(pipefd[0]); //子进程 void Writer(int wfd) { } //父进程 void Reader(int rfd) { } Writer //子进程 void Writer(int wfd) { string s = "hello,I am child"; pid_t self = getpid(); int number = 0; char buffer[10]; while(true) { buffer[0] = 0;//字符串清空,只是为了提醒阅读代码的人,我把这个数组当字符串了 } } 用到snprintf 介绍 将s和self和number放进buffer char buffer[100]; while(true) { buffer[0] = 0;//字符串清空,只是为了提醒阅读代码的人,我把这个数组当字符串了 snprintf(buffer,sizeof(buffer),"%s pid:%d\n",s.c_str(),self); cout<< buffer < sleep(1); }; 用cout打印测试一下,打印成功说明写入buffer成功了 等待进程少不了,子进程exit后需要回收 //父进程 close(pipefd[1]); //.....IPC code,写通信代码 //在pipefd[0]这个管道里写 Reader(pipefd[0]); //等待进程缺少不了 pid_t rid = waitpid(id,nullptr,0); if(rid < 0) return 3;//等待失败了 close(pipefd[0]); 如何把消息发送/写入给父进程 用到了write 用write写入管道(管道也是文件),用strlen,不用+1,不用管\0,因为C语言规定\0结尾,和文件没有关系,wfd写入管道 //子进程 void Writer(int wfd) { string s = "hello,I am child"; pid_t self = getpid(); int number = 0; char buffer[100]; while(true) { buffer[0] = 0;//字符串清空,只是为了提醒阅读代码的人,我把这个数组当字符串了 snprintf(buffer,sizeof(buffer),"%s pid:%d %d\n",s.c_str(),self,number++); //用write写入管道(管道也是文件),用strlen,不用+1,不用管\0,因为C语言规定\0结尾,和文件没有关系,wfd写入管道 write(wfd,buffer,strlen(buffer)); //cout<< buffer < sleep(1); }; } 父进程该怎么读取呢 用到了read,fd是文件描述符,从特定的文件描述符里读取,放在这个buf里,buf的长度是count 这里就需要考虑到\0,因为buffer中需要\0 //父进程 void Reader(int rfd) { char buffer[100]; while(true) { buffer[0] = 0; //用sizeof是为了留个空间放\0 ssize_t n = read(rfd, buffer, sizeof(buffer));//sizeof!=strlen if(n > 0) { //添加\0,因为要放在buffer数组中读取 buffer[n]=0; cout << "father get a message[" << getpid() <<"]"<< buffer < } } } 运行结果 也会发现:为什么子进程sleep,父进程不sleep,父进程还是会跟着子进程sleep,因为父子进程是要协同的 管道本质 通信是为了更好的发送变化的数据,管道本质上是文件 所以必须要用到系统调用接口来访问管道,其是由系统管理,read和write ,操作系统相当于中介 结论:管道的特征: 1:具有血缘关系的进程进行进程间通信 2:管道只能单向通信 3:父子进程是会进程协同的,同步与互斥的--保护管道文件的数据安全 4:管道是面向字节流的 5:管道是基于文件的,而文件的生命周期是随进程的 再测试,把子进程sleep去掉,就是让子进程写快一点,父进程sleep几秒,就是让父进程读慢一点,看有什么现象 管道的四种情况 测试管道大小 把c一直往管道里写,把父进程中休眠50秒 结果差不多64kb 写端退了,测试结果 结果是: 读端正常读,写端关闭,读端就会读到0,表明读到了文件(pipe)结尾,不会被阻塞 read读取成功会返回读到的字符个数,读到结尾返回0 读到结尾父进程也就可以停止读取了,break后去把僵尸的子进程回收 break到这里 最后子进程会被waitpid回收 测试子进程一直写,父进程读一会就退出 定义一个cnt控制退出的时间 这里也要修改一下,加个sleep(5),观察,close提前关闭 结果:通过13号信号杀死 管道到的应用场景 都会变成一个进程 写一个进程池(pipe_use) 首先创建好文件 创建5个进程 channel通道的意思 cmdfd文件描述符 slaverid代表哪个子进程 把它放进vector容器里 思路步骤 管道创建 void(n),假装使用一下,要不然编译不过 创建父子进程 父进程写,子进程读 子进程要读取,就要关闭自己的写端,父进程同理 子进程中的任务 子进程pid有了管道也有了,就差在父进程添加字段了 先更改一下,在class里构造一下 添加字段 测试一下:结果:文件描述符0,1,2是默认打开,3是从管道里读,4是写入管道 把初始化改造成函数 debug测试函数,纯输入函数 第二步开始控制进程了(想让子进程做什么) 这里打印的rfd都是3,正常吗,文件描述符是可以被子进程继承的 父进程对应的写端拿到的是4-8,子进程拿到的读端fd是3 改变一下,直接从键盘(0号描述符)里读,不从管道(3)里读了,就没有管道的概念了,slaver就不用传参了,父进程通过管道写,子进程通过标准输入读 用到了dup2,将从pipefd[0]中读变成从0开始读 想让父进程固定的向管道里写入指定大小字节的内容,必须读取四个字节,四个字节四个字节的写和读,这里的管道64kb 必须读取四个字节 如果父进程不给子进程发送数据呢?阻塞等待! 开始控制子进程 生成一个随机数种子 可以随机选择任务和选择进程 cmd是任务码,测试一下,父进程控制子进程,父进程发送给子进程(通过cmdcode连续) 在Task.hpp里 要用到函数指针 main中的任务了就属于 再把任务装载进来 输出型参数用* 现在开始选择任务和进程 再把main中的任务弄成全局的 进行判断一下 测试 ,comcode和任创建的任务一致 这里的write是父进程进行写入,向子进程发送,子进程不得闲,先写到管道里,等得闲了再读 也可以轮询选择,定义一个计数器,++弄,再%等 整理一下控制代码,这里是输入型参数,只需要读 这样就可以轮询方式选择进程了,不用随机了 结果 清理收尾 思路:把所有文件的描述符都关掉 等待方式设置为0 read返回0,就是失败了,然后slaver就会调完 结束完就会exit直接退出 打印下更好显示 关闭文件描述符后sleep(10)秒, 然后这10个子进程一瞬间都应该break,然后最后到exit直接就退了,10秒结束后,父进程再回收他 测试时不弄死循环,用cnt,5秒后自动结束控制,正常退出流程 测试结果 手动控制一下 定义一个select,输入0就是退出了,判断完后,就走到了选择任务 然后直接把cmdcode改为选择的select,-1是因为是从下标0开始的,输入1就是0下标的 测试 bug的地方: 这样会有一些bug(一个子进程不是只有一个写端(每一次子进程的创建都是有继承)) 这样会有一些bug(一个子进程不是只有一个写端(每一次子进程的创建都是有继承)) 按理说这样是对的,可是这样就错了 因为下面两个红线还没有关掉,它们进程了最开始的w 这样倒着回收是可以的 正确改法 修改一下 最后一个push_back的就都是父进程的写入fd, 然后加一句这个红线的,每创建子进程后都先把上一次父进程的读端fd关掉就可以了,这里很妙,因为vector一开始是空的 方便看 这里这样就可以了 管道已经完成 以上是匿名管道 总文件总代码 makefile中代码 ProcessPool:ProcessPool.cc g++ -o $@ $^ -std=c++11 .PHNOY:clean clean: rm -f ProcessPool Task.hpp中代码 #pragma once #include #include using namespace std; typedef void (*task_t)(); void task1() { cout<< "lol 刷新日志" < } void task2() { cout<< "lol 更新野区" < } void task3() { cout<< "lol 检测软件更新" < } void task4() { cout<< "lol 释放技能" < } ProcessPool.cc中代码 注意: 这里为啥是取地址一个comcode,不是一个0吗,要发送的数据就是这个,所以要把地址传给函数,可以是个数组 换成数组的话,read这也接收数据的时候,就得用数组去接受,要是写入超过int大小的话,就可能会出错,这个就是通信的双方要遵守的约定,这个判断一下,就是派发的这个任务是不是合法的,假设你的tasks任务中,只有4个任务,所以任务编号就是0 ~ 3,如果你接受到的任务编号是10或者-20,那么这些就是非法的,你执行的话,程序就会崩溃,所以要做一个简单的判断。 write以后,cmdcode的值也会跟着传到read对吧,write就是为了把cmdcode的值传递给给另外一个进程,以前见到的都是用的char buffer[];,这样&cmdcode能更方便的传值过去是不,看要传的是什么数据,只是传递一个int数据的话,就这样传递,如果是文本数据,或者是其他的话,可能就需要数组了,具体问题,具体讨论 #include "Task.hpp" #include #include #include #include #include #include #include using namespace std; //打算创建5个进程 const int processnum = 5; //全局任务 vector //先描述 class channel//管道 { public: channel(int cmdfd,pid_t slaverid,string& processname) :_cmdfd(cmdfd) ,_slaverid(slaverid) ,_processname(processname) {} public: int _cmdfd;//文件描述符 pid_t _slaverid;//代表哪个子进程 string _processname;//子进程的名字,方便打印日志 }; //子进程中读的任务 // void slaver(int rfd) // { // while(true) // { // cout<< getpid() <<" - "<< "read fd is->"< // sleep(1000); // } // } //改变一下从fd为0的地方开始读 void slaver() { //read(0); while(true) { int cmdcode = 0; int n = read(0, &cmdcode, sizeof(int)); if(n == sizeof(int)) { //执行cmdcode对应的任务列表 cout<< "slaver say@ get a command:" << getpid() << ":cmdcode:" << cmdcode < //判断一下并执行 if(cmdcode >= 0 && cmdcode < tasks.size()) tasks[cmdcode](); } if(n == 0) break; } } //初始化 void Init(vector { for(int i =0;i < processnum;i++) { int pipefd[2]; int n = pipe(pipefd);//创建管道 //返回值小于0就创建失败了 assert(!n); (void)n; pid_t id = fork(); if(id == 0) { //子进程:读 close(pipefd[1]); //改变一下从fd为0的地方读 dup2(pipefd[0],0); close(pipefd[0]); //任务 slaver(); cout<< "process: " << getpid() << "quit" < //slaver(pipefd[0]); exit(0); } //父进程:写 close(pipefd[0]); //channel添加字段 string name = "processs-" + to_string(i); //插入的是自定义类型,要构造一下,第一个传的是文件描述符,要写入的fd channels.push_back(channel(pipefd[1], id, name)); } } //测试函数,纯输入函数 //输入:const & //输出:* //输入输出:& void debug(const vector { for(auto&e : channels) { cout<< e._cmdfd <<" "< } } void Loadtask(vector { tasks->push_back(task1); tasks->push_back(task2); tasks->push_back(task3); tasks->push_back(task4); } void memu() { cout<< "########################" < cout<< "1:lol 刷新日志 2:lol 更新野区" < cout<< "1:lol 检测软件更新 4:lol 释放技能" < cout<< " 0:退出 " < cout<< "########################" < } //2:开始控制子进程 void ctrlSlaver(vector { int which = 0; int cnt = 5; while(true) { int select = 0; memu(); cout<< "Please Enter@:"; cin>> select; if(select == 0) break; //1:选择任务 //int cmdcode = rand()%tasks.size(); int cmdcode = select - 1; //2:随机选择进程 //int processpos = rand()%channels.size(); //2:轮询选择进程 cout<< "father say:"<< "cmdcode:" << cmdcode << " already sendto " < < //3:发送任务 write(channels[which]._cmdfd, &cmdcode, sizeof(cmdcode)); which++; which%=channels.size();//保证不大于其长度 cnt--; if(cnt == 0) break; sleep(1); } } void QuitProcess(const vector { for(const auto& e : channels) close(e._cmdfd); sleep(10); for(const auto& e : channels) waitpid(e._slaverid, nullptr, 0);//进程的pid=_slaverid,关上了以后记得回收 } int main() { Loadtask(&tasks); //srand(time(nullptr)^getpid()^1023);//种一个随机数种子 //在组织 vector //1:初始化 Init(channels); debug(channels); //2:开始控制子进程 ctrlSlaver(channels); //3:清理收尾 QuitProcess(channels); return 0; } 管道是 毫不相关的进程进程间通信::命名管道 命名管道 https://blog.csdn.net/2401_83427936/article/details/142603367 首先自己要用用户层缓冲区,还得把用户层缓冲区拷贝到管道里,(从键盘里输入数据到用户层缓冲区里面),然后用户层缓冲区通过系统调用(write)写到管道里,然后再通过read系统调用,被对方(读端)读取,就要从管道拷贝到读端,然后再显示到显示器上。 mkfifo命名管道 1号手册是指令,。2号系统调用接口 创建一个管道,p开头就是命名管道,并不会直接刷新到磁盘中,实际是个符号 这样会阻塞 这样会显示出来(先输入左边的,再输入右边的就会显示),左右两边是两个进程 >>追加写入的方式,但空间一直是0 所以这就是文件里大小一直是0的原因 你怎么知道打开的是同一个文件 正好符合前提 所以要创建两个可执行程序,各自跑各自的,创建一个common是为了方便使用头文件 client是客户 server是服务者 makefile中一下运行两个程序 mkfifo,用程序的方式创建管道,第一个参数是要创建的这个管道在那个路径下叫什么名字,也就是要保持唯一性的那些点,第二个是创建一个管道 这里是3号手册是函数。 返回 -1创建失败 创建一个共享文件 ./myfifo server.cc和client.cc想看到同一个文件,包含上头文件就可以了 这里先用server控制管道文件 创建管道失败了设置为1 ,如果失败了就exit(1) 谁控制的先运行运行谁就好了 make一下生成两个可执行程序,因为是server控制的,所以要先运行server 运行后就会多一个myfifo命名管道 命名管道的删除 想删除这个myfifo用unlink(成功返回0 ,失败返回-1) 命令行删除 代码也可以删(成功返回0 ,失败返回-1),头文件是unistd.h 创建完文件,5秒后就删除了 思路 用到了open 打开管道文件,第二个参数是只进行读取 enum中 fd<0打开失败了 服务端读取数据 客户端,只要用就行 第二个参数就是打开文件为了写入的 用户输入完成以后,就要发送输入的消息到另一端 打开顺序一定 然后打开的顺序就一定了,先打开server,然后再打开另一个cc 先打开服务端,会阻塞在这里,然后再打开客户端,进行输入 右边输入啥,左边就会有啥 无法输入空格问题(getline) getline (string) - C++ Reference (cplusplus.com) 但有一个问题就是cin没法输入空格,,要用到getline 会发现一个问题,客户端退出了,服务端还没退出 客户端退出,会read到0,所以服务端(读端)也要退出 改正 sever端 等待写入方式打开后,自己才会打开文件,向后执行,open阻塞了! 优化一下 写成进程池的样子 日志 创建一个新文件 用到了可变参数(形参实例化是从右到左) 可变参数必须右至少一个具体的参数 举个例子:步骤:s指向可变部分 这里的sum第一个参数是几个数求和的意思,传不同的类型不可以的,因为上面va_arg里已经写死了 开始写日志,level日志等级 先定义时间,time,时间戳 ctime ctime - C++ Reference (cplusplus.com) 头文件 tm_year年tm_min分tm_huor小时 打印具体年月日 年是从1900年开始的 年月日时分秒 vsnprint vsnprint,跟不带v的区别就是,去除了...换成了可变参数部分 把日记等级转换成字符串风格,所有有可能的地方都需要返回 改进 va_start(s,format),用format修饰s的指向,上面的sum是(s,n),类似 这里要用c_str,因为返回的是string 用完以后再用end 这里是往显示器打印的,这里要*3,以为%s和%s中间有几个空格,空间不够 把这里修改一下,打开失败的话 这样就形成日志了 打印最后一行就是正常打开 这里也改一下 测试,先./server,然后会阻塞,然后./client,就会打印出,logmessage里的信息 为啥./client之前不打印 因为等待写入方式打开后,自己才会打开文件,向后执行,open阻塞了! 往文件里打印(上面是往屏幕打印) 先把这些内容全放在Log,日志类 分类 1:向屏幕打印 2:向一个文件打印 3:分类打印 打印格式printMethod 这里构造默认往屏幕去印 析构 打印方式也改一下 打印单个 以0666的权限打开这个文件 打印多个文件(添加一下level) 实现一下 优化一下 以后再打印日志就不用这样打了 这样就可以了,要记住先创建一个Log对象 这样以后就写入一个文件了,写入log.txt 这样就把日志分类了 结果 但是日志文件这么多太混乱了 这样操作后就统一进入一个文件了 makefile也修改一下,先把path定义的log目录创建一下 日志放入一个文件测试结果: 日志分类测试结果: log.hpp里头文件 优化一下调用 然后修改一下server.cc client.cc #include "common.hpp" #include "log.hpp" int main() { int fd = open(FIFO_FILE,O_WRONLY); if(fd < 0) { perror("open"); exit(FIFO_OPEN_ERR); } string line; while(true) { cout<< "Please Enter@ "; // cin>> line; getline(cin, line); write(fd, line.c_str(),line.size()); } close(fd); return 0; } common.hpp #pragma noce #include #include #include #include #include #include #include #include #include using namespace std; #define FIFO_FILE "./myfifo" #define MODE 0664 //用于设置文件的权限,0664代表着8进制写法,4是其他用户可读不可写 enum { FIFO_CREATE_ERR = 1, FIFO_DELETE_ERR, FIFO_OPEN_ERR }; class Init { public: Init() { //创建管道 int n = mkfifo(FIFO_FILE,MODE); if(n == -1) { perror("mkfofi"); exit(FIFO_CREATE_ERR); } } ~Init() { //删除命名管道 int m = unlink(FIFO_FILE); if(m == -1) { perror("unlink"); exit(FIFO_DELETE_ERR); } } }; log.hpp #pragma noce #include // #include "common.hpp" #include #include #include #include using namespace std; #define Info 0 #define Debug 1 #define Warning 2 #define Error 3 #define Fatal 4 // 致命的 //打印方式 #define Screen 1 //屏幕 #define Onefile 2 //一个文件 #define Classfile 3 //多个文件 #define LogFile "log.txt" class Log { public: Log() { printMehod = Screen; path = "./log/"; } void Enable(int method) { printMehod = method; } string levelToString(int level) { switch (level) { case Info: return "Info"; case Debug: return "Debug"; case Warning: return "Warning"; case Error: return "Error"; case Fatal: return "Fatal"; default: return ""; } return ""; } // void logmessage(int level, const char *format, ...) // { // time_t t = time(nullptr); // struct tm *ctime = localtime(&t); // char leftbuffer[1024]; // snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%d-%d-%d %d:%d:%d]", levelToString(level).c_str(), // ctime->tm_year + 1900, ctime->tm_mon + 1, ctime->tm_mday, ctime->tm_hour, ctime->tm_min, ctime->tm_sec); // va_list s; // va_start(s, format); // char rightbuffer[1024]; // vsnprintf(rightbuffer, sizeof(rightbuffer), format, s); // va_end(s); // // 格式:默认部分+自定义部分 // char logtxt[1024 * 3]; // snprintf(logtxt, sizeof(logtxt), "%s %s\n", leftbuffer, rightbuffer); // //cout << logtxt << endl; // 暂时打印 // printLog(level, logtxt); // } void operator()(int level, const char* format, ...) { time_t t = time(nullptr); struct tm *ctime = localtime(&t); char leftbuffer[1024]; snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%d-%d-%d %d:%d:%d]", levelToString(level).c_str(), ctime->tm_year + 1900, ctime->tm_mon + 1, ctime->tm_mday, ctime->tm_hour, ctime->tm_min, ctime->tm_sec); va_list s; va_start(s, format); char rightbuffer[1024]; vsnprintf(rightbuffer, sizeof(rightbuffer), format, s); va_end(s); // 格式:默认部分+自定义部分 char logtxt[1024 * 3]; snprintf(logtxt, sizeof(logtxt), "%s %s\n", leftbuffer, rightbuffer); //cout << logtxt << endl; // 暂时打印 printLog(level, logtxt); } void printLog(int level, const string &logtxt) { switch(printMehod) { case Screen: cout<< logtxt < break; case Onefile: printOneFile(LogFile, logtxt);//"log.txt" break; case Classfile: printClassFile(level, logtxt); break; default: break; } } void printOneFile(const string &logname, const string &logtxt) { // "./log/" "log.txt" string _logname =path + logname; int fd = open(_logname.c_str(), O_WRONLY|O_CREAT|O_APPEND, 0666); if(fd < 0) return; write(fd, logtxt.c_str(), logtxt.size()); close(fd); } void printClassFile(int level, const string &logtxt) { string filename = LogFile;//"log.txt" filename += ".";//"log.txt." filename += levelToString(level); //log.txt.Debug/Waring/Fatal printOneFile(filename, logtxt); } ~Log() {} private: int printMehod; string path; }; makefile .PHONY:all all:client server server:server.cc g++ -o $@ $^ -g -std=c++11 mkdir log client:client.cc g++ -o $@ $^ -g -std=c++11 .PHONY:clean clean: rm -f server client server.cc #include "common.hpp" #include "log.hpp" int main() { //logmessage(Info, "hello"); //创建管道 Init init; Log log; //log.Enable(Onefile); log.Enable(Classfile); // //创建管道 // int n = mkfifo(FIFO_FILE,MODE); // if(n == -1) // { // perror("mkfofi"); // exit(FIFO_CREATE_ERR); // } // sleep(5); //打开管道 int fd = open(FIFO_FILE,O_RDONLY); if(fd < 0) { //log.logmessage(Fatal, "error string:%s,error code:%d",strerror(errno), errno); //优化后 log(Fatal, "error string:%s,error code:%d",strerror(errno), errno); exit(FIFO_OPEN_ERR); } // log.logmessage(Info, "server open file done,error string:%s,error code:%d",strerror(errno), errno); // log.logmessage(Warning, "server open file done,error string:%s,error code:%d",strerror(errno), errno); //优化后 log(Info, "server open file done,error string:%s,error code:%d",strerror(errno), errno); log(Warning, "server open file done,error string:%s,error code:%d",strerror(errno), errno); //...... //开始通信 while(true) { char buffer[1024] = {0}; int x = read(fd, buffer, sizeof(buffer)); if(x > 0) { buffer[x] = 0; cout<< "client say# " << buffer < } else if(x == 0) { //log.logmessage(Debug, "sclient quit too!,error string:%s,error code:%d",strerror(errno), errno); //优化后 log(Debug, "sclient quit too!,error string:%s,error code:%d",strerror(errno), errno); //cout<< "client quit too!\n" < break; } else break; } close(fd); // //删除命名管道 // int m = unlink(FIFO_FILE); // if(n == -1) // { // perror("unlink"); // exit(FIFO_DELETE_ERR); // } return 0; }