linux进程间通信实现剪刀石头布-命名管道

linux进程间通信实现剪刀石头布-命名管道

为何要写这篇文章?

因为本人在操作课程上需要写这么个东西,查阅网络资料,发现似乎只有一个源码!
全网就那么一个源码,抄都没得抄。
https://blog.csdn.net/weixin_33672109/article/details/94240244
连接放在这里。
这段代码没有问题,可以正常运行,但可惜的是,他是一次性生成了100个随机数塞进管道里,随后再由裁判统一判定,这样显然不是真实的猜拳流程,人不可能虚空划拳。
老师也要求程序应当有“轮”的概念,在本人捣鼓了半晚上之后,终于凑合捏了个东西出来造福人类。(反正我已经打完分了233)

linux进程间通信的方案那么多,你用的是啥?

FIFO/命名管道
为啥,因为简单呗

正文

首先我在google上找到了一个FIFO通信的原型,这货允许两个进程间互相发送信息

https://www.geeksforgeeks.org/named-pipe-fifo-example-c-program/

还要啥自行车,在他的基础上改改呗~


judger.c

#include <stdio.h> 
#include <string.h> 
#include <fcntl.h> 
#include <sys/stat.h> 
#include <sys/types.h> 
#include <unistd.h> 
#include <time.h>
int judge(char a,char b)//判定规则
{
 int r=0;
 if(a==b)
 r=0;
 else
 {
   if(a=='0'&&b=='1')r=1;
   if(a=='0'&&b=='2')r=-1;
   if(a=='1'&&b=='2')r=1;
   if(a=='1'&&b=='0')r=-1;
   if(a=='2'&&b=='0')r=1;
   if(a=='2'&&b=='1')r=-1;
 }
 return r;
}
 
  
  
int main() 
{ 
    int fd1,fd2,fd3,fd4; 
    // FIFO file path 
    char * myfifo1 = "/tmp/myfifo1"; 
	char * myfifo2 = "/tmp/myfifo2"; 
	char * myfifo3 = "/tmp/myfifo3"; 
    char * myfifo4 = "/tmp/myfifo4"; 
    // Creating the named file 
    mkfifo(myfifo1, 0666); 
	mkfifo(myfifo2, 0666); 
    mkfifo(myfifo3, 0666); 
	mkfifo(myfifo4, 0666); 
    
    char arr1[1],arr2[1]; 
    char start[1] = {'5'};
    char end[1] = {'7'};
    printf("i'm judger!\n");
    int l = 0;
    
    int draw=0;
    int pw1=0;
    int pw2=0;
    
    while (1) 
    { 
        l++;
        if(l>100){ //end
                fd1 = open(myfifo1, O_WRONLY); 
                fd2 = open(myfifo2, O_WRONLY);
                write(fd1, end, strlen(start)+1); 
                write(fd2, end, strlen(start)+1);
                close(fd1); 
                close(fd2); 
                break;
            }
        printf("---------this is the %d loop---------\n",l);
        // Open write only
        fd1 = open(myfifo1, O_WRONLY); 
				fd2 = open(myfifo2, O_WRONLY);
      
        // Write 
        // and close
        write(fd1, start, strlen(start)+1); 
        write(fd2, start, strlen(start)+1); //start !!
        close(fd1); 
        close(fd2); 
		
        usleep(10);
        // Open Read only 
        fd3 = open(myfifo3, O_RDONLY); 
				fd4 = open(myfifo4, O_RDONLY); 
        // Read from FIFO 
        read(fd3, arr1, sizeof(arr1)); 
				read(fd4, arr2, sizeof(arr2)); 
        printf("player1: %c\n", arr1[0]); 
        printf("player2: %c\n", arr2[0]);
        close(fd3); 
        close(fd4); 
        
        
        int tmp = judge(arr1[0],arr2[0]);
        if(tmp==0)
         {
          printf("in a draw!\n");//平局
          draw++;
         }
         else
         {
          printf("%s wins!\n",(tmp>0)?"p1":"p2");
          if(tmp>0){
              pw1++;}else{
                  pw2++;}
         } 
         
    } 
    printf("--------------\nsummary:\nthe draw:%d\nplayer1 win:%d\nplayer2 win:%d\n--------------\n",draw,pw1,pw2);
    
    
    return 0; 
}


player1.c

#include <stdio.h> 
#include <string.h> 
#include <fcntl.h> 
#include <sys/stat.h> 
#include <sys/types.h> 
#include <unistd.h> 
#include <stdlib.h>
#include <time.h>

int main() 
{ 
	int fd1,fd3; 

	// FIFO file path 
	char * myfifo1 = "/tmp/myfifo1"; 
	char * myfifo3 = "/tmp/myfifo3"; 
	mkfifo(myfifo1, 0666); 
	mkfifo(myfifo3, 0666); 
	
	char str1[1]; 
	int l = 0;
	srand(time(NULL));
	while (1) 
	{ 
		usleep(5);
		l++;
		printf("---------this is the %d loop---------\n",l);
		fd1 = open(myfifo1,O_RDONLY); 
		
		read(fd1, str1, 1); 

		printf("judge: %c --> ", str1[0]); 
		close(fd1); 
		
		if(str1[0] == '5'){
			fd3 = open(myfifo3,O_WRONLY); 
			int j = rand()%3;
			str1[0] = j + '0';
			write(fd3, str1, strlen(str1)+1); 
			printf("dict=%d\n",j);
			close(fd3); 
			}else if(str1[0] == '7'){
				printf("exit\n");
				break;
				}
		
	} 
	return 0; 
} 


player2.c

#include <stdio.h> 
#include <string.h> 
#include <fcntl.h> 
#include <sys/stat.h> 
#include <sys/types.h> 
#include <unistd.h> 
#include <stdlib.h>
#include <time.h>

int main() 
{ 
	int fd2,fd4; 

	// FIFO file path 
	
	char * myfifo2 = "/tmp/myfifo2"; 
	char * myfifo4 = "/tmp/myfifo4"; 
	
	mkfifo(myfifo2, 0666); 
	mkfifo(myfifo4, 0666); 
	
	char str2[1]; 
	srand((unsigned)(time(NULL)+100));
	int l=0 ;
	while (1) 
	{ 	
		usleep(10);
		l++;
		printf("---------this is the %d loop---------\n",l);
	
		fd2 = open(myfifo2,O_RDONLY); 
		
		read(fd2, str2, 1); 
		printf("judge: %c --> ", str2[0]); 
		close(fd2); 
		if(str2[0] == '5'){
			fd4 = open(myfifo4,O_WRONLY); 
			int j = rand()%3;
			str2[0] = j + '0';
			write(fd4, str2, strlen(str2)+1); 
			printf("dict=%d\n",j);
			close(fd4); 
			}else if(str2[0] == '7'){
				printf("exit\n");
				break;
				}


	} 
	return 0; 
} 

ok,到此,功能就可以实现了。
两个player完全可以使用fork()合并在同一个文件里,但由于一晚上时间紧迫,本人又没学过c,故放弃。


一些坑

c语言随机数使用srand(time(NULL))来定义种子,时间精确到秒,所以可能会发生seed相同的情况,故不同进程的srand请 + 一个数字,区分开来。

srand在循环外侧使用一次即可!!!千万别放在循环里面,每次重新初始化会导致大量重复值

rand()返回值为int,需要转换为char rand()%3 + '0' 注意是单引号。
char使用printf()的%d打印可能会出现一些奇怪的乱码,请用%c

sleep()函数在win下单位是毫秒 linux下是秒,1s太久,我们只争朝夕啊,所以用usleep()
不过FIFO管道似乎会自动阻塞,所以其实不需要延迟的
我仍不知道管道自己会不会自动清空,但看起来应该是会的,在read之后,管道里啥都木得了。

运行时请打开三个终端一起运行鸭

在这里插入图片描述
哦 这基佬紫色的终端~


事情到这里应该就结束了,不过我的室友看到我在这里写文章,他也想投入到这造福人类的伟大事业之中来,因此再提供一个版本的代码

使用fork()的版本
#include <unistd.h>
#include<fcntl.h>
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<errno.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>
#include<sys/wait.h>

int judge(char a,char b);

int main(void) 
{ 
 int fd1,fd2,fd3,fd4,i=0;
 int status;
 char * myfifo1 = "/tmp/myfifo1";
 char * myfifo2 = "/tmp/myfifo2";
 char * myfifo3 = "/tmp/myfifo3";
 char * myfifo4 = "/tmp/myfifo4";
 char start[1] = {'3'};
 char end[1] = {'4'};
 char c1[1];//用来存放p1发送的消息
 char c2[2];//用来存放p2发送的消息
 char s1[1];//用来存放裁判发送的消息
 char s2[2];//用来存放裁判发送的消息
 int p1w=0,p2w=0,pd=0;

/**************************************/
//创建4个管道
if((mkfifo(myfifo1,0666)<0)&&(errno!=EEXIST))
{
   printf("cannot create fifo.\n");
   exit(1);
}
if((mkfifo(myfifo2,0666)<0)&&(errno!=EEXIST))
{
   printf("cannot create fifo.\n");
   exit(1);
}
if((mkfifo(myfifo3,0666)<0)&&(errno!=EEXIST))
{
   printf("cannot create fifo.\n");
   exit(1);
}
if((mkfifo(myfifo4,0666)<0)&&(errno!=EEXIST))
{
   printf("cannot create fifo.\n");
   exit(1);
}

 /*********************************/
//产生子进程p1
 pid_t p1 = fork(); 
 /*********************************/
 if(p1==0)
 { 
  srand(time(NULL));
  while(1)
  {
   i++;
   fd1 = open(myfifo1,O_RDONLY);
   read(fd1,s1,1);
   close(fd1);
   if(s1[0] == '3')
   {
    fd3 = open(myfifo3,O_WRONLY);
    int j = rand()%3;
    s1[0] = j + '0';
    write(fd3,s1,strlen(s1)+1);
    close(fd3);
    
   }else if(s1[0] == '4'){
    exit(0);    
   }
  }
  return 0;
 }
/*********************************/

//产生子进程p2,程序结构同p1
 pid_t p2=fork(); 
/*********************************/
 if(p2==0)
 { 
  srand(time(NULL)+100);
  while(1)
  {
   i++;
   fd2 = open(myfifo2,O_RDONLY);
   read(fd2,s2,1);
   close(fd2);
   if(s2[0] == '3')
   {
    fd4 = open(myfifo4,O_WRONLY);
    int j = rand()%3;
    s2[0] = j + '0';
    write(fd4,s2,strlen(s2)+1);
    close(fd4);
    
   }else if(s2[0] == '4'){
 
    exit(0);    
   }
  }
  return 0;
 }

//比赛场地

while(1)
{
if(p1 != 0 && p2!=0){
 i++;
 if(i>100)
 {
  fd1 = open(myfifo1,O_WRONLY);
  fd2 = open(myfifo2,O_WRONLY);
  write(fd1,end,strlen(start)+1);
  write(fd2,end,strlen(start)+1);
  close(fd1);
  close(fd2);
  break;
 }
 printf("这是第 %d 轮\n",i);
 //只写打开命名管道1,2
 fd1 = open(myfifo1,O_WRONLY);
 fd2 = open(myfifo2,O_WRONLY);
 //向管道1,2中写入开始出拳信号start
 write(fd1,start,strlen(start+1));
 write(fd2,start,strlen(start+1));
 close(fd1);
 close(fd2);
 sleep(1);
 //只读打开命名管道3,4
 fd3 = open(myfifo3,O_RDONLY);
 fd4 = open(myfifo4,O_RDONLY);
 //从管道3,4中读出比赛选手出拳情况
 read(fd3,c1,sizeof(c1));
 read(fd4,c2,sizeof(c2));
 //打印一轮结果
 printf("p1: %c\n",c1[0]);
 printf("p2: %c\n",c2[0]);
 close(fd3);
 close(fd4);
 
int tmp = judge(c1[0],c2[0]);
 if(tmp==0)
  {
   printf("in a draw!\n");
   pd++;//平局
  }
  else
  {
   printf("%s wins!\n",(tmp>0)?"p1":"p2");
   if(tmp>0){
    p1w++;//p1胜
    }
   else {
   p2w++;    //p2胜
   }
  }

} 
}
//打印最终统计结果
printf("In summary:\n");
printf("p1 wins %d  rounds.\n",p1w);
printf("p2 wins %d  rounds.\n",p2w);
printf("%d rounds end in a draw.\n",pd);
printf("%s wins in the game!\n",(p1w>p2w)?"p1":"p2");
 
}
 
//0——石头,1——剪刀,2——布
int judge(char a,char b)//规定游戏判定规则
{
 int r=0;
 if(a==b){
 r=0;}
 else
 {
   if(a=='0'&&b=='1')r=1;
   if(a=='0'&&b=='2')r=-1;
   if(a=='1'&&b=='2')r=1;
   if(a=='1'&&b=='0')r=-1;
   if(a=='2'&&b=='0')r=1;
   if(a=='2'&&b=='1')r=-1;
 }
 return r;
}

在这里插入图片描述

不过有个缺点,就是每秒才能生成一轮,虽然不知道他具体在哪里出了点小问题,总之我也懒得管了,你们自己看着去改吧。

就酱
溜了溜了

  • 28
    点赞
  • 100
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
里面一、课程设计(论文)题目 :石头-剪刀-布游戏    二、课程设计(论文)工作自 2014 年 6 月23日起至 2014 年 6 月 27日止 三、课程设计(论文) 地点: 8-501 四、课程设计(论文)内容要求: 1. 本课程设计的目的 (1)进一步巩固和加深对“C++程序设计”课程基本知识的理解和掌握,了解C++语言在项目开发中的应用。 (2)学习程序设计开发的一般方法,了解和掌握项目开发过程及方式,培养正确的设计思想和分析问题、解决问题的能力,特别是项目设计能力。 (3)通过对标准化、规范化文档的掌握并查阅有关技术资料等,培养项目设计开发 能力,同时提倡团队合作精神。 2. 课程设计的任务及要求 1) 基本要求: (1) 对系统功能进行需求分析; (2) 使用Visual C++编译器进行程序编译; (3) 提出系统的设计方案; (4) 编写源程序代码并进行调试。 2) 创新要求 在基本要求达到后,可进行创新设计。 3) 课程设计报告撰写及装订要求 课程设计报告的撰写要求表述简明,图表准确。 报告按如下内容和顺序用A4纸进行打印并装订成册。 (1)封面 采用统一的课程设计封面,并按要求填写好封面要求的个人信息和选题。 (2)设计任务书 (3)评阅书 (4)目录 (5)正文 (6)主要参考文献 4) 课程设计评分标准: 每人按指定题目进行设计,严禁抄袭,要求每人自己动手编写程序,采取同一组同时检查程序及运行结果,检查时同组成员每人陈述自己的分工,同一选题不同组如发现代码完全一样,则双方都作不及格处理。 (1)达到课程设计的目的与要求,程序的可读性较好,并调试正确,60分; (2)能正确回答设计的中老师所提问题,可增加10分; (3)课程设计报告书写规范整齐,可增加10分; (4)心得体会认真总结,可增加10分; (5)程序有创新性,可增加10分; 成绩评定实行优秀、良好、中等、及格和不及格五个等级。不及格者需重做。 5) 课程设计进度安排 (1)准备阶段(3学时):选择设计题目、了解设计目的要求、查阅相关资料。 (2)程序模块设计分析阶段(4学时):程序总体设计、详细设计。 (3) 代码编写调试阶段(10学时):程序模块代码编写、调试、测试。 (4) 撰写论文阶段(3学时):总结课程设计任务和设计内容,撰写课程设计论文。 6)课程设计题目具体要求: 在游戏中,孩子们用手表示石头、剪刀或布中的一个,由拳头表示石头,伸出两根手指表示剪刀,伸手表示布,孩子们面对面地从1数到3时做出他们的选择,如果所作选择是一样的,则表示平局,否则就按如下规则决定胜负。 (1)石头砸坏剪刀。 (2)剪刀剪碎布 (3)布覆盖石头 编程实现计算机与人进行游戏。并输出游戏结果。如果屏幕输出笑脸,则表示人胜利;如果屏幕输出哭脸,则表示人失败;输出无奈,则表示平局。并且统计结果。 包含着关于石头剪刀布的设计方案以及改进等等问题

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值