作者的废话:Linux课程设计要求使用符合Linux特性来写一个小代码,只要求在终端上运行,看表格上好多同学不是写关于命令的,就是文件的复制操作之类的,还有一大半的聊天系统,我实现想不出来要写什么,就随便报了个使用管道通信的三端(一个服务端,两个客户端)猜数字小游戏,有趣的是我们老师还说这个在网上有很多,后来想看看(抄袭X)别人怎么写的,结果在CSDN和GitHub上都没找到类似的,最后自己写了个初始的(还有一堆bug的,别问,问就是Linux课上在学其他东西,嘤嘤嘤),后面哪怕去找AI都解决不了(GPT3.5是真的无语辣),最后还是去请教舍友后才把bug解决了(bug主要是因为管道少了,造成两客户端和服务端相互竞争,缓冲区相互读写覆盖了)。
运行效果:
首先运行服务器端的代码,再运行两个客户端的代码,当一个客户端猜测的时候,另一个可不能偷跑哦~所以输不了数字,只能轮流输入。服务器端会对猜测的数字进行判断,而两个客户端也会给出更新的数字区间进行提示。
至于代码的讲解,我写了非常~非常详细的注释哦~
代码如下:
服务器端代码
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<time.h>
#include<string.h>
//宏定义三个管道
#define PLAYER1_PIPE "player1_pipe"
#define PLAYER2_PIPE "player2_pipe"
#define PLAYER3_PIPE "player3_pipe"
//服务器
int main(){
//管道文件1、2、3
int player1_fd, player2_fd,player3_fd;
int min_number = 1; //数字区间下界
int max_number = 100; //数字区间上界
int secret_number; //秘密数字
char buffer[10]; //缓冲区
//以时间戳生成秘密数字(1-100)
srand(time(NULL));
secret_number = rand() % 100 + 1;
printf("秘密数字: %d\n", secret_number);
//创建命名管道
mkfifo(PLAYER1_PIPE, 0666);
mkfifo(PLAYER2_PIPE, 0666);
mkfifo(PLAYER3_PIPE, 0666);
//打开管道
player1_fd = open(PLAYER1_PIPE, O_RDWR);
player2_fd = open(PLAYER2_PIPE, O_RDWR);
player3_fd = open(PLAYER3_PIPE, O_RDWR);
while(1)
{//当有玩家猜对数字时结束循环,既game over~
//向玩家1发送当前的数字范围
sprintf(buffer, "%d-%d", min_number, max_number);
write(player1_fd, buffer, sizeof(buffer)); //写入缓冲区
//从管道3中读取玩家1的猜测
read(player3_fd, buffer, sizeof(buffer));
int player3_guess = atoi(buffer); //将字符串buffer转换为整数
//对玩家1的猜测进行判断
if(player3_guess == secret_number)
{
printf("玩家1获胜!\n");
sprintf( buffer,"%s","e");
//玩家1猜对数字,服务器发送代表玩家1获胜的字符e给管道1和2
write(player1_fd, buffer, sizeof(buffer));
write(player2_fd, buffer, sizeof(buffer));
break;
}else if(player3_guess > secret_number && player3_guess < max_number){//更新上界
printf("玩家1猜测的数字太大辣~\n");
max_number = player3_guess - 1;
}else if(player3_guess < secret_number && player3_guess > min_number){//更新下界
printf("玩家1猜测的数字太小辣~\n");
min_number = player3_guess + 1;
}else if(player3_guess > max_number){
printf("数字超过区间上界了哦~");
}else if(player3_guess < min_number){
printf("数字超过区间下界了哦~");
}
//把数字区间上界和下界转换成字符串存储到缓冲区buffer中
sprintf(buffer, "%d-%d", min_number, max_number);
write(player2_fd, buffer, sizeof(buffer)); //发送给玩家2
//从管道3中读取玩家2的猜测
read(player3_fd, buffer, sizeof(buffer));
player3_guess = atoi(buffer); //转回数字
//判断玩家2的猜测
if(player3_guess == secret_number)
{
printf("玩家2获胜!\n");
sprintf( buffer,"%s","f");
//玩家2猜对数字,服务器发送代表玩家2获胜的字符e给管道1和2
write(player2_fd, buffer, sizeof(buffer));
write(player1_fd, buffer, sizeof(buffer));
break;
}else if(player3_guess > secret_number && player3_guess < max_number){ //更新上界
printf("玩家2猜测的数太大辣~\n");
max_number = player3_guess - 1;
}else if(player3_guess < secret_number && player3_guess > min_number){ //更新下界
printf("玩家2猜测的数字太小辣~\n");
min_number = player3_guess + 1;
}else if(player3_guess > max_number){
printf("数字超过区间上界了哦~");
}else if(player3_guess < min_number){
printf("数字超过区间下界了哦~");
}
}
//关闭三个管道,释放资源
close(player1_fd);
close(player2_fd);
close(player3_fd);
//删除三个管道
unlink(PLAYER1_PIPE);
unlink(PLAYER2_PIPE);
unlink(PLAYER3_PIPE);
return 0;
}
客户端1的代码:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<errno.h>
#define PLAYER1_PIPE "player1_pipe"
#define PLAYER3_PIPE "player3_pipe"
//玩家1端
int main(){
//打开两个管道,可读写模式
int player1_fd = open(PLAYER1_PIPE, O_RDWR);
int player3_fd = open(PLAYER3_PIPE, O_RDWR);
char buffer[10];
while(1)
{
//从管道1中读取数据
ssize_t bytes_read = read(player1_fd, buffer, sizeof(buffer));
//缓冲区中有数据才会进入判断,当缓冲区没有数据时配合usleep挂起,以达成轮流输入
//当玩家1输入时,玩家2不能输入,陷入死等,只有等玩家1输入完后玩家2才可以输入
if(bytes_read > 0)
{
if(buffer[0] == 'e')
{
printf("Victory!\n");
break;
}else if(buffer[0] == 'f'){
printf("Defeat!\n");
break;
}
printf("猜测数字区间: %s\n", buffer);
int min_number, max_number;
sscanf(buffer, "%d-%d", &min_number, &max_number); //从字符串中解析出两个数字,提示玩家1猜数区间
//输入玩家1的猜测
int guess;
printf("玩家1,请输入你的猜测: ");
scanf("%d", &guess);
sprintf(buffer, "%d", guess); //转换成字符串
//写入管道3中
write(player3_fd, buffer, sizeof(buffer));
}else if(bytes_read == 0){
//提示玩家2退出游戏
printf("玩家2跑路辣!\n");
break;
}else if(errno != EAGAIN && errno != EWOULDBLOCK){
//非阻塞I/O操作错误,资源不可用,比如当管道不存在时报个错~
perror("read error");
break;
}
//暂停执行一毫秒,避免忙等
usleep(1000);
}
//关闭管道1和3
close(player1_fd);
close(player3_fd);
return 0;
}
客户端2的代码:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<errno.h>
#define PLAYER2_PIPE "player2_pipe"
#define PLAYER3_PIPE "player3_pipe"
//玩家2端
int main(){
//打开管道2和3
int player2_fd = open(PLAYER2_PIPE, O_RDWR);
int player3_fd = open(PLAYER3_PIPE, O_RDWR);
char buffer[10];
while(1)
{
//从管道2中读取数据
ssize_t bytes_read = read(player2_fd, buffer, sizeof(buffer));
if(bytes_read > 0)
{
if(buffer[0] == 'f')
{
printf("Victory!\n");
break;
}else if(buffer[0] == 'e'){
printf("Defeat!\n");
break;
}
printf("猜测数字区间: %s\n", buffer);
//给出数字区间上下界
int min_number, max_number;
sscanf(buffer, "%d-%d", &min_number, &max_number);
int guess;
printf("玩家2,请输入你的猜测: ");
scanf("%d", &guess); //玩家2输入猜测
//写入管道3中
sprintf(buffer, "%d", guess);
write(player3_fd, buffer, sizeof(buffer));
}else if(bytes_read == 0){
//提示玩家1退出游戏
printf("玩家1跑路辣!\n");
break;
}else if(errno != EAGAIN && errno != EWOULDBLOCK){
//非阻塞I/O操作错误,同玩家1
perror("read error");
break;
}
//挂起
usleep(1000);
}
//关闭管道2和3
close(player2_fd);
close(player3_fd);
return 0;
}