Linux_c 有名管道练习
题目:
使用有名管道实现两个进程间的双向通讯
程序设计:
1、程序以 ./a.out A B 和./a.out B A 的方式运行,且只编写一份源码;
2、当./a.out A B 启动程序:(以./a.out B A则反过来)
1)生成一个A_B.fifo的管道文件(A->B),创建一个B_A.fifo的管道文件(A<-B);
2)创建线程send_task,打开A_B.fifo,实现向B发送消息;
3)创建线程recv_task,打开B_A.fifo文件,实现接收来自B的消息
3、当某一方放松quit时,双方退出程序并销毁相关的资源
实现代码(多线程版本):
//sT-发送线程,rT-接收线程
pthread_t sT,rT;
//判断程序入参的正确性
int checkArgs(int argc, const char*argv[]);//OK-返回0,失败-返回-1
//创建管道文件,若不存在,则新建;若存在,直接返回
void createFifo(char *filename, const char *from,const char *to);
//发送数据线程执行函数
void* sendTask(void* argv);
//接收数据线程执行函数
void* recvTask(void* argv);
int main(int argc, const char *argv[])
{
//判断入参的合法性
if(checkArgs(argc, argv) != 0){
return -1;
}
//创建两个管道文件
char sendFile[20];
createFifo(sendFile, argv[1], argv[2]);
//printf("sendFile=[%s]\n", sendFile);
char recvFile[20];
createFifo(recvFile, argv[2], argv[1]);
//printf("recvFile=[%s]\n", recvFile);
//开启发送和接受线程
pthread_t sT,rT;
pthread_create(&sT, NULL, sendTask, sendFile);
pthread_create(&rT, NULL, recvTask, recvFile);
//
//销毁资源
pthread_join(sT, NULL);
pthread_join(rT, NULL);
printf("程序已经关闭\n");
return 0;
}
int checkArgs(int argc, const char*argv[]){
if(argc!=3){
printf("参数输入错误:必须以./a.out A B或./a.out B A的方式运行\n");
return -1;
}
if(strcmp(argv[1],"A")==0&&strcmp(argv[2],"B")==0)
return 0;
if(strcmp(argv[1],"B")==0&&strcmp(argv[2],"A")==0)
return 0;
printf("参数输入错误:必须以./a.out A B或./a.out B A的方式运行\n");
return -1;
}
void createFifo(char *filename, const char *from, const char *to){
sprintf(filename,"%s_%s.fifo", from, to);
if(mkfifo(filename, 0644)==0){
//printf("管道文件[%s]创建成功\n", filename);
return;
}
//否则,mkfifo返回错误码
if(errno == EEXIST){//文件已经存在,这个错误直接无视掉
//printf("管道文件[%s]已经存在,无须创建\n", filename);
return;
}
perror("文件创建失败,程序即将退出");
exit(EXIT_FAILURE);
}
void* sendTask(void* argv){
//printf("In sendTask: %s\n", (char*)argv);
//以写的方式打开管道文件
int fd=open((char*)argv,O_WRONLY);
if(fd == -1){
perror("sendTask 文件打开失败");
return NULL;
}
//
char buf[1024];
while(1){
memset(buf,0,sizeof(buf));
//从命令行终端获取用户输入的字符串
//printf("请输入>>:");
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1]=0;//不要\n
//将字符串发送到指定管到中
ssize_t cnt=write(fd,buf,strlen(buf));
if(cnt == -1){
perror("写管道异常");
break;
}
//如果发送quit指令
if(strcmp("quit",buf)==0){
break;
}
}
//关闭自己
close(fd);
pthread_exit(NULL);
}
void* recvTask(void* argv){
//printf("In recvTask: %s\n", (char*)argv);
//以读的方式打开管道文件
int fd=open((char*)argv, O_RDONLY);
if(fd == -1){
perror("recvTask:文件打开失败");
return NULL;
}
char buf[1024];
while(1){
//接收来自管道的消息
memset(buf,0,sizeof(buf));
ssize_t cnt=read(fd,buf,sizeof(buf));
if(cnt == -1){
perror("管道读异常");
break;
}
printf("收到消息:%s\n", buf);
//如果收到quit指令
if(strcmp("quit", buf)==0){
//cancel发送线程线程
//pthread_cancel(sT);
//exit(EXIT_SUCCESS);
//
break;
}
}
//关闭自己
close(fd);
pthread_exit(NULL);
}
运行效果:
遗留问题:
两个终端要分别发送quit,双方程序才能完全终止。原因大致为,程序有两个线程,一个负责发,一个负责收,发数据的线程,需要通过fgets从终端获取数据,因此在客户没有输入内容的时候,会阻塞住。且一个线程退出后,没有办法让另一个线程也停止。暂时还没有什么更好的办法。