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

下面这个程序利用命名管道实现进程间通信,模拟石头剪刀布游戏。

主进程为裁判进程,两个子进程为选手进程。裁判与选手间各建立一个命名管道。

进行100次出招,最后给出游戏胜负。


#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>
#define FIFO1 "/tmp/myfifo1"
#define FIFO2 "/tmp/myfifo2"
#define SIZE 5
#define COUNT 100
int judge(char a,char b);
int main(void) 
{ 
 int fp,fp1,fp2,i=1;
 int status;
 int nread;
 char buf[SIZE]={0};
 char c1[COUNT]={0};//用来存放p1发送的消息
 char c2[COUNT]={0};//用来存放p2发送的消息
 pid_t p1 = fork(); //产生子进程p1
 /*********************************/
 if(p1==0)
 { 
  srand(time(NULL));
  while((fp=open(FIFO1,O_WRONLY|O_NONBLOCK))==-1);//只写打开管道1,不断尝试直到成功
  for(;i<=100;i++)
  {
    sprintf(buf,"%d",rand()%3);//随机产生0-2的数字写入管道
    while(write(fp,buf,SIZE)==-1);//不断尝试写直至成功
  }
  close(fp);
  return 0; 
 } 
/*********************************/
 pid_t p2=fork(); //产生子进程p2,程序结构同p1
/*********************************/
 if(p2==0) 
 { 
  srand(time(NULL)+100);
  while((fp=open(FIFO2,O_WRONLY|O_NONBLOCK))==-1);
  for(;i<=100;i++)
  {
    sprintf(buf,"%d",rand()%3);
    while(write(fp,buf,SIZE)==-1);
  }
  close(fp);
  exit(0);
 }

/**************************************/
//创建2个管道
if((mkfifo(FIFO1,0777)<0)&&(errno!=EEXIST))
{
   printf("cannot create fifo.\n");
   exit(1);
}
if((mkfifo(FIFO2,0777)<0)&&(errno!=EEXIST))
{
   printf("cannot create fifo.\n");
   exit(2);
}

memset(buf,0,sizeof(buf));//清空缓冲区
//只读方式打开两个命名管道
while((fp1=open(FIFO1,O_RDONLY|O_NONBLOCK,0))==-1);
while((fp2=open(FIFO2,O_RDONLY|O_NONBLOCK,0))==-1);

sleep(3);//等待两个子进程中打开管道写端并输入数据,必要
for(;i<=100;i++)//连续读取100个数据
{
  nread=read(fp1,buf,SIZE);
  if(nread!=-1&&nread!=0)
  {
   c1[i]=buf[0];//结果存放至c1
  }
}
i=1;
for(;i<=100;i++)
{
  nread=read(fp2,buf,SIZE);
  if(nread!=-1&&nread!=0)
  {
   c2[i]=buf[0];//结果存放至c2
  }
}


int j=1;
int p1w=0,p2w=0,pd=0;
for(;j<=100;j++)
{
 int tmp=judge(c1[j],c2[j]);
 printf("round %d:",j);
 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");

//等待两个子进程结束
 if(waitpid(p1,&status,0) < 0)
 {
    perror("waitpid");
    exit(5);
 }
 if(waitpid(p2,&status,0)< 0)
 {
    perror("waitpid");
    exit(6);
 }
 exit(0);
}

//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;
}




结果:

<think>嗯,用户想用Linux的进程间通信实现石头剪刀游戏。我需要先确定如何用IPC机制来实现这个游戏的不同进程之间的通信。首先,石头剪刀游戏需要两个玩家进程,可能还需要一个裁判进程来处理逻辑和结果判定。常用的IPC方法有管道、消息队列、共享内存、信号量、套接字等。这里可能需要双向通信,所以消息队列可能比较合适,或者管道的话可能需要多个管道。 用户提供的示例中有消息队列的引用,所以可能优先考虑消息队列。消息队列允许不同进程发送和接收消息,适合这种回合制的游戏。每个玩家的选择需要发送到裁判进程,裁判比较后返回结果。或者,也可以让两个玩家直接通过消息队列交换信息,但裁判的存在可能更便于管理。 接下来需要考虑进程的结构。比如,父进程作为裁判,创建两个子进程作为玩家。玩家进程生成随机选择,发送给裁判,裁判接收后比较,然后发送结果给玩家。或者,玩家之间直接通信,但需要协调顺序,避免竞争条件。 消息队列的使用步骤包括创建队列、发送消息、接收消息。每个消息需要包含玩家ID和选择的内容。消息结构体可以定义动作类型,比如石头剪刀,用枚举表示。然后裁判进程根据两个玩家的选择判断胜负。 代码示例方面,可能需要三个进程:裁判和两个玩家。或者,两个玩家进程和一个裁判进程,通过消息队列通信。比如,玩家1发送选择到队列,玩家2发送到同一个队列,裁判读取两个消息后比较,并发送结果到另一个队列,或者直接输出。 需要注意同步问题,比如裁判需要等待两个玩家的消息都到达后再处理。可能需要使用消息类型来区分不同的玩家,或者用不同的队列。例如,每个玩家发送到裁判的队列,裁判收集两个消息后处理。 另外,可能需要信号量或其他同步机制来确保消息的顺序和处理正确。但为了简化示例,可能暂时不考虑错误处理和复杂的同步,只演示基本功能。 可能的代码结构:裁判进程创建消息队列,循环等待接收两个玩家的选择,比较后输出结果。玩家进程生成随机选择,发送消息到队列,然后等待裁判的结果消息。或者,玩家发送后直接退出,裁判处理完即结束。 需要定义消息的结构,例如: struct message { long mtype; // 消息类型,1表示玩家1,2表示玩家2 int choice; // 0-石头,1-剪刀,2- }; 裁判根据mtype接收两个消息,然后比较choice的值,判断胜负。但需要确保裁判能正确接收到两个玩家的消息,可能需要循环接收特定类型的消息,或者用不同的消息类型区分玩家。 另外,玩家可能需要发送后等待裁判的结果,所以可能需要另一个消息队列来返回结果。或者,使用同一个队列,但裁判发送结果消息给玩家。 但这样可能比较复杂。或者,将结果打印出来即可,不需要返回给玩家进程,仅作为示例。 在示例代码中,可能需要用到msgget, msgsnd, msgrcv等函数。玩家进程生成随机数,发送到队列,裁判读取后比较。 可能存在多个队列:一个用于玩家发送选择给裁判,另一个用于裁判发送结果给玩家,或者使用同一队列,但消息类型不同。 或者,可以简化处理,让裁判处理完后直接输出结果到标准输出,而不需要反馈给玩家进程。这样玩家只需要发送选择,裁判处理并输出结果即可。 此外,需要考虑消息队列的权限和删除,避免残留的消息队列影响后续运行。所以在代码结束时,需要删除队列。 可能的代码步骤: 1. 创建消息队列(裁判进程) 2. 玩家进程连接到该队列 3. 每个玩家发送自己的选择(包含玩家ID和选择) 4. 裁判接收两个消息,比较后输出结果 5. 删除消息队列 示例代码可能需要三个独立的程序:裁判、玩家1、玩家2。或者,用fork创建子进程来模拟玩家。 但用户可能希望一个完整的示例,所以可能需要编写三个部分的代码,或者在一个程序中使用fork分出裁判和玩家进程。 总之,需要构建一个简单的示例,使用消息队列实现两个玩家发送选择,裁判接收并判断胜负。</think>### Linux进程间通信实现石头剪刀游戏示例 #### 实现思路 1. **使用消息队列**作为通信方式(基于引用[2]的示例扩展)[^2] 2. 创建三个进程: - 裁判进程:管理消息队列,判断胜负 - 玩家进程A/B:生成随机手势并通过消息队列发送 3. 定义消息结构体: ```c struct msg_buffer { long msg_type; // 1=玩家A,2=玩家B int choice; // 0=石头,1=剪刀,2= }; ``` #### 示例代码(裁判进程) ```c #include <stdio.h> #include <stdlib.h> #include <sys/ipc.h> #include <sys/msg.h> struct msg_buffer { long msg_type; int choice; }; int main() { key_t key = ftok("game", 65); int msgid = msgget(key, 0666 | IPC_CREAT); struct msg_buffer msg; int results[2], count = 0; while(count < 2) { msgrcv(msgid, &msg, sizeof(msg), 0, 0); if(msg.msg_type == 1) results[0] = msg.choice; if(msg.msg_type == 2) results[1] = msg.choice; count++; } if((results[0]+1)%3 == results[1]) printf("玩家A胜\n"); else if((results[1]+1)%3 == results[0]) printf("玩家B胜\n"); else printf("平局\n"); msgctl(msgid, IPC_RMID, NULL); return 0; } ``` #### 玩家进程代码(以玩家A为例) ```c #include <stdio.h> #include <stdlib.h> #include <sys/ipc.h> #include <sys/msg.h> #include <time.h> struct msg_buffer { long msg_type; int choice; }; int main() { srand(time(0)); key_t key = ftok("game", 65); int msgid = msgget(key, 0666 | IPC_CREAT); struct msg_buffer msg; msg.msg_type = 1; // 玩家A标识 msg.choice = rand() % 3; msgsnd(msgid, &msg, sizeof(msg), 0); return 0; } ``` #### 实现步骤 1. 编译三个程序:`gcc referee.c -o referee`, `gcc playerA.c -o playerA`, `gcc playerB.c -o playerB` 2. 先运行裁判进程:`./referee` 3. 另开两个终端分别运行:`./playerA` 和 `./playerB`
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

聚沙塔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值