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