基于消息队列+多进程编写的银行模拟系统

银行模拟系统

  • 概述
  • [客户端 client.c](#客户端 client.c)
  • [服务端 serve.c](#服务端 serve.c)
  • [开户 enroll.c](#开户 enroll.c)
  • [存款 save.c](#存款 save.c)
  • [转账 transfer.c](#转账 transfer.c)
  • [取款 take.c](#取款 take.c)
  • makefile文件

概述

该案例大体过程为,服务器先启动,初始化消息队列和信号,用多线程技术启动开户、存钱、转账、取钱模块,并且可以控制结束。客户端启动后会自动链接服务器,会将用户的请求发送到请求队列,从响应消息队列读取处理结果,并提示给用户。其他模块轮询检测请求消息队列中是否有自己应该去做事情的消息,如果读取到就开始任务,并且将任务结果发送到响应消息队列。

目前用户信息存取是通过txt文件实现的,每个用户单独一个文件。

并且提供了makefile文件生成可执行文件,文件布局如下:

客户端 client.c

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int reqid;//请求队列id
int resid;//响应队列id

typedef struct msgbuff
{
   long mtype;
   char name[20];
   char password[20];
   float money;
}user;

typedef struct resbuff
{
   long type;
   char name[20];
   char msg[256];
   float money;
}res;

void menu(void);				//菜单
void enroll(void);				//开户
void save_money(void);			//存钱
void transfer_money(void);		//转账
void take_money(void);			//取钱

int main(int argc,char *argv[])
{
   while(1)
   {
   	menu();
   }
}


void menu(void)
{
   system("clear");
   printf("\n\t欢迎使用wwz银行ATM机\n");
   printf("\t\t1:开户\n");
   printf("\t\t2:存钱\n");
   printf("\t\t3:转账\n");
   printf("\t\t4:取钱\n");
   printf("\t请输入1-4进行选择:\n");
   int key;
   scanf("%d",&key);
   switch(key)
   {
   	case 1 :	enroll();			break;
   	case 2 :	save_money();		break;
   	case 3 :	transfer_money();	break;
   	case 4 :	take_money();		break;
   default:printf("输入有误请重新输入\n");sleep(1);break;
   }
}
void enroll(void)
{
   system("clear");
   //键值
   key_t key1 = ftok("/home/wwz/xyd/bank/out",1);
   key_t key2 = ftok("/home/wwz/xyd/bank/out",2);
   //打开请求消息队列
   reqid = msgget(key1,IPC_CREAT|0644);
   if(reqid== -1){perror("msgget");return;}
   //打开应答消息队列
   resid =msgget(key2,IPC_CREAT|0644);
   if(resid == -1){perror("msgget");return;}
   //读取信息
   printf("请输入用户名、密码、存钱金额\n");
   user u;
   scanf("%s %s %f",u.name,u.password,&u.money);
   //发送到请求队列
   u.mtype = 1;
   msgsnd(reqid,&u,sizeof(user)-sizeof(long),0);
   //接收请求结果
   res r;
   msgrcv(resid,&r,sizeof(res),1,0);  //1对应上面的1
   printf("%s",r.msg);
   return;
}
void save_money(void)
{
   system("clear");
   //键值
   key_t key1 = ftok("/home/wwz/xyd/bank/out",1);
   key_t key2 = ftok("/home/wwz/xyd/bank/out",2);
   //打开请求消息队列
   reqid = msgget(key1,IPC_CREAT|0644);
   if(reqid== -1){perror("msgget");return;}
   //打开应答消息队列
   resid =msgget(key2,IPC_CREAT|0644);
   if(resid == -1){perror("msgget");return;}
   //读取信息
   printf("请输入用户名、密码、存钱金额\n");
   user u;
   scanf("%s %s %f",u.name,u.password,&u.money);
   //发送到请求队列
   u.mtype = 2;
   msgsnd(reqid,&u,sizeof(user)-sizeof(long),0);
   //接收请求结果
   res r;
   msgrcv(resid,&r,sizeof(res),2,0);  //1对应上面的1
   printf("%s",r.msg);
   return;
}
void transfer_money(void)
{
   system("clear");
   //键值
   key_t key1 = ftok("/home/wwz/xyd/bank/out",1);
   key_t key2 = ftok("/home/wwz/xyd/bank/out",2);
   //打开请求消息队列
   reqid = msgget(key1,IPC_CREAT|0644);
   if(reqid== -1){perror("msgget");return;}
   //打开应答消息队列
   resid =msgget(key2,IPC_CREAT|0644);
   if(resid == -1){perror("msgget");return;}
   //读取信息
   printf("请输入用户名、密码、转账金额、转账账户\n");
   user u1,u2;
   scanf("%s %s %f %s",u1.name,u1.password,&u1.money,u2.name);
   //发送到请求队列
   u1.mtype = 4;
   u2.mtype = 5;
   msgsnd(reqid,&u1,sizeof(user)-sizeof(long),0);
   msgsnd(reqid,&u2,sizeof(user)-sizeof(long),0);
   //接收请求结果
   res r1,r2;
   msgrcv(resid,&r1,sizeof(res),4,0);
   msgrcv(resid,&r2,sizeof(res),5,0);
   printf("%s\t",r1.msg);
   printf("%s",r2.msg);
   return;
}
void take_money(void)
{
   system("clear");
   //键值
   key_t key1 = ftok("/home/wwz/xyd/bank/out",1);
   key_t key2 = ftok("/home/wwz/xyd/bank/out",2);
   //打开请求消息队列
   reqid = msgget(key1,IPC_CREAT|0644);
   if(reqid== -1){perror("msgget");return;}
   //打开应答消息队列
   resid =msgget(key2,IPC_CREAT|0644);
   if(resid == -1){perror("msgget");return;}
   //读取信息
   printf("请输入用户名、密码、取钱金额\n");
   user u;
   scanf("%s %s %f",u.name,u.password,&u.money);
   //发送到请求队列
   u.mtype = 3;
   msgsnd(reqid,&u,sizeof(user)-sizeof(long),0);
   //接收请求结果
   res r;
   msgrcv(resid,&r,sizeof(res),3,0);  //1对应上面的1
   printf("%s",r.msg);
   return;
}

服务端 serve.c

c 复制代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
#include <stdlib.h>

static int reqid = -1; //作为请求的消息队列的返回id
static int resid = -1;  //作为响应的消息队列的返回id

typedef struct Service{
    char path[256];
    pid_t pid;
}Srv;
static Srv srv[]={ 
    {"./out/enroll",-1},      	//开户
    {"./out/save",-1},      	//存款
    {"./out/take",-1},  		//取款
    {"./out/trans",-1},     	//转账
};

int Init_msg();				//初始化消息队列
void deInit_msg();			//销毁消息队列
void sigint(int signum);	//初始化信号
int start();				//启动服务器
int stop();					//关闭服务器

int main(int argc,char *argv[])
{
	atexit(deInit_msg);
	signal(SIGINT,sigint);
	if(Init_msg() == -1){return -1;}
    if(start() == -1){return -1;}
    sleep(1);
    //阻塞等待退出
    printf("按<回车>键退出......\n");
    getchar();
    if(stop() == -1){return -1;}
    return 0;
}
int Init_msg()
{
    printf("服务器初始化.....\n");
    
    //键值
	key_t key1 = ftok("/home/wwz/xyd/bank/out",1);
	key_t key2 = ftok("/home/wwz/xyd/bank/out",2);
	//创建
    reqid = msgget(key1,IPC_CREAT|0644);
	if(reqid == -1){perror("msgget");return -1;}
    printf("创建请求消息队列成功!\n");
    resid = msgget(key2,IPC_CREAT|0644);
	if(resid == -1){perror("msgget");return -1;}
    printf("创建应答消息队列成功!\n");
    return 0;
}
void deInit_msg()
{
    printf("服务器关闭.....\n");
    if(msgctl(reqid,IPC_RMID,NULL) == -1){perror("msgctl");}
    else	printf("销毁请求消息队列成功!\n");
    if(msgctl(resid,IPC_RMID,NULL) == -1)perror("msgctl");
    else	printf("销毁应答消息队列成功!\n");
}
void sigint(int signum)
{
    printf("%d\n",signum);
    stop();
    exit(0);
}

int start()
{
    printf("启动服务器\\n");
    size_t i;
    for(i=0; i<sizeof(srv)/sizeof(srv[0]); i++)
    {
        if((srv[i].pid = vfork()) == -1)
        {perror("vfork");return -1;}
        if(srv[i].pid == 0)
        {
            if(execl(srv[i].path,srv[i].path,NULL)==-1)
            {
                perror("execl");
                return -1;
            }
            return 0;
        }
    }
    return 0;
}
int stop()
{
    printf("关闭服务器\n");
    size_t i;
    for(i=0; i<sizeof(srv)/sizeof(srv[0]); i++)
    {
        if(kill(srv[i].pid,SIGINT) == -1)
        {
            perror("kill");
            return -1;
        }
    }
    for(;;)if(wait(NULL) == -1){perror("wait");break;}
    return 0;
}

开户 enroll.c

c 复制代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>

int reqid; 
int resid; 

typedef struct msgbuff
{
	long mtype;
	char name[20];
	char password[20];
	float money;
}user;

typedef struct resbuff
{
	long type;
	char name[20];
	char msg[256];
	float money;
}res;
//停止服务函数
void stop_fun(void)
{
	printf("开户服务停止\n");
	exit(0);
}
//保存信息函数
int save(const user *u)
{
	char pathname[256] = {0};
	sprintf(pathname,"/home/wwz/xyd/bank/data/%s.txt",u->name);
	FILE * fp = fopen(pathname,"w+");
	if(fp == NULL){perror("save:fopen");return -1;}
	fprintf(fp,"%s\t%s\t%lf\n",u->name,u->password,u->money);
	fclose(fp);
	return 0;
}

int main(int argc,char *argv[])
{
	signal(SIGINT,(void *)stop_fun);
	//键值
	key_t key1 = ftok("/home/wwz/xyd/bank/out",1);
	key_t key2 = ftok("/home/wwz/xyd/bank/out",2);
	//打开请求消息队列
	reqid = msgget(key1,IPC_CREAT|0644);
	if(reqid == -1){perror("msgget");return -1;}
	//打开应答消息队列
	resid = msgget(key2,IPC_CREAT|0644);
	if(resid == -1){perror("msgget");return -1;}
	
	printf("开户服务启动\n");
	while(1)
	{
		//轮循等待从队列中读出数据
		user req;
		if(msgrcv(reqid,&req,sizeof(req)-sizeof(long),1,0)==-1)
		{perror ("msgrcv");continue;}
		
		printf("开户帐号:");
        printf("%s\n",req.name);
		
		res r;
		strcpy(r.name,req.name);
		//打开文件并且确认是否有该用户
		char temp[128]={0};
		sprintf(temp,"/home/wwz/xyd/bank/data/%s.txt",req.name);
		//只写,如果打不开就证明没有,能打开就是有了
		int fp = open(temp,O_WRONLY);
		
		if(fp>0)
		{
			r.type = 1;
			sprintf(r.msg,"已有该用户,开户失败!");
			msgsnd(resid,&r,sizeof(res)-sizeof(long),0);
		}
		else
		{
			save(&req);
			sprintf(r.msg,"开户成功!");
			msgsnd(resid,&r,sizeof(res)-sizeof(long),0);
		}
		close(fp);
	}
	return 0;
}

存款 save.c

c 复制代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>

int reqid; 
int resid; 

typedef struct msgbuff
{
	long mtype;
	char name[20];
	char password[20];
	float money;
}user;

typedef struct resbuff
{
	long type;
	char name[20];
	char msg[256];
	float money;
}res;

void * stop_fun(void)
{
	printf("存款服务停止\n");
	exit(0);
}

int get(char name[20],user *u)
{
	char pathname[256] = {0};
	sprintf(pathname,"/home/wwz/xyd/bank/data/%s.txt",name);
	FILE * fp = fopen(pathname,"r");
	if(fp == NULL){perror("get:fopen");return -1;}
	fscanf(fp,"%s\t%s\t%f\n",u->name,u->password,&u->money);
	fclose(fp);
	return 1;
}

int save(const user *u)
{
	char pathname[256] = {0};
	sprintf(pathname,"/home/wwz/xyd/bank/data/%s.txt",u->name);
	FILE * fp = fopen(pathname,"w+");
	if(fp == NULL){perror("save:fopen");return -1;}
	fprintf(fp,"%s\t%s\t%lf\n",u->name,u->password,u->money);
	fclose(fp);
	return 0;
}

int main(int argc,char *argv[])
{
	signal(SIGINT,(void *)stop_fun);
	//键值
	key_t key1 = ftok("/home/wwz/xyd/bank/out",1);
	key_t key2 = ftok("/home/wwz/xyd/bank/out",2);
	//打开请求消息队列
	reqid = msgget(key1,IPC_CREAT|0644);
	if(reqid == -1){perror("msgget");return -1;}
	//打开应答消息队列
	resid = msgget(key2,IPC_CREAT|0644);
	if(resid == -1){perror("msgget");return -1;}
	
	printf("存款服务启动\n");
	while(1)
	{
		//轮循等待从队列中读出数据
		user req;
		if(msgrcv(reqid,&req,sizeof(req)-sizeof(long),2,0)==-1)
		{perror ("msgrcv");continue;}
        printf("存款帐号:%s\n",req.name);
		res r;
		user u;
		if(get(req.name,&u)==-1)
		{sprintf(r.msg,"无效账户");goto send;}
		
		if(strcmp(req.password,u.password)!=0)
		{sprintf(r.msg,"密码错误");goto send;}
		
		u.money += req.money;
		if(save(&u) == -1)
		{sprintf(r.msg,"存款失败");goto send;}
		r.money = u.money;
		sprintf(r.msg,"存款成功");
send:		
		r.type = 2;
		if(msgsnd(resid,&r,sizeof(res)-sizeof(long),0)==-1)
        {perror("msgsnd");continue;}
	}
	return 0;
}

转账 transfer.c

c 复制代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>

int reqid; 
int resid; 

typedef struct msgbuff
{
	long mtype;
	char name[20];
	char password[20];
	float money;
}user;

typedef struct resbuff
{
	long type;
	char name[20];
	char msg[256];
	float money;
}res;

void  stop_fun(void)
{
	printf("转账服务停止\n");
	exit(0);
}

int get(char name[20],user *u)
{
	char pathname[256] = {0};
	sprintf(pathname,"/home/wwz/xyd/bank/data/%s.txt",name);
	FILE * fp = fopen(pathname,"r");
	if(fp == NULL){perror("get:fopen");return -1;}
	fscanf(fp,"%s\t%s\t%f\n",u->name,u->password,&u->money);
	fclose(fp);
	return 1;
}

int save(const user *u)
{
	char pathname[256] = {0};
	sprintf(pathname,"/home/wwz/xyd/bank/data/%s.txt",u->name);
	FILE * fp = fopen(pathname,"w+");
	if(fp == NULL){perror("save:fopen");return -1;}
	fprintf(fp,"%s\t%s\t%lf\n",u->name,u->password,u->money);
	fclose(fp);
	return 0;
}

int main(int argc,char *argv[])
{
	
	signal(SIGINT,(void *)stop_fun);
	//键值
	key_t key1 = ftok("/home/wwz/xyd/bank/out/",1);
	key_t key2 = ftok("/home/wwz/xyd/bank/out/",2);
	//打开请求消息队列
	reqid = msgget(key1,IPC_CREAT|0644);
	if(reqid == -1){perror("msgget");return -1;}
	//打开应答消息队列
	resid = msgget(key2,IPC_CREAT|0644);
	if(resid == -1){perror("msgget");return -1;}
	
	while(1)
	{
		//轮循等待从队列中读出数据
		user req1,req2;
		if(msgrcv(reqid,&req1,sizeof(user)-sizeof(long),4,0)==-1)
		{perror ("req1:msgrcv");continue;}
		if(msgrcv(reqid,&req2,sizeof(user)-sizeof(long),5,0)==-1)
		{perror ("req2:msgrcv");continue;}
		
		res r1,r2;
		user u1,u2;
		
		memset(r1.msg,0,sizeof(r1.msg));
		memset(r2.msg,0,sizeof(r2.msg));
		
		printf("转账帐号:%s  收款帐号:%s\n",req1.name,req2.name);
		
		if(get(req1.name,&u1)==-1)
		{strcpy(r1.msg,"转账账户为无效账户");goto send;}
		if(get(req2.name,&u2)==-1)
		{strcpy(r2.msg,"收款账户为无效账户");goto send;}
		if(strcmp(req1.password,u1.password))
		{strcpy(r1.msg,"密码错误");goto send;}
		if(req1.money>u1.money)
		{sprintf(r1.msg,"抱歉,余额不足,您的余额为:%f",u1.money);goto send;}
		u1.money -= req1.money;
		u2.money += req1.money;
		if(save(&u1) == -1)
		{strcpy(r1.msg,"转账失败");goto send;}
		if(save(&u2) == -1)
		{strcpy(r2.msg,"收款失败");goto send;}
		
		r1.money = u1.money;
		r2.money = u2.money;
		
		sprintf(r1.msg,"转账成功,您的余额为:%f",u1.money);
		strcpy(r2.msg,"收款成功");
send:		
		r1.type = 4;
		if(msgsnd(resid,&r1,sizeof(res)-sizeof(long),0)==-1)
        {perror("msgsnd");continue;}
send2:
		r2.type = 5;
		if(msgsnd(resid,&r2,sizeof(res)-sizeof(long),0)==-1)
        {perror("msgsnd");continue;}
	}
	return 0;
}

取款 take.c

c 复制代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>

int reqid; 
int resid; 

typedef struct msgbuff
{
	long mtype;
	char name[20];
	char password[20];
	float money;
}user;

typedef struct resbuff
{
	long type;
	char name[20];
	char msg[256];
	float money;
}res;

void  stop_fun(void)
{
	printf("取款服务停止\n");
	exit(0);
}

int get(char name[20],user *u)
{
	char pathname[256] = {0};
	sprintf(pathname,"/home/wwz/xyd/bank/data/%s.txt",name);
	FILE * fp = fopen(pathname,"r");
	if(fp == NULL){perror("get:fopen");return -1;}
	fscanf(fp,"%s\t%s\t%f\n",u->name,u->password,&u->money);
	fclose(fp);
	return 1;
}

int save(const user *u)
{
	char pathname[256] = {0};
	sprintf(pathname,"/home/wwz/xyd/bank/data/%s.txt",u->name);
	FILE * fp = fopen(pathname,"w+");
	if(fp == NULL){perror("save:fopen");return -1;}
	fprintf(fp,"%s\t%s\t%lf\n",u->name,u->password,u->money);
	fclose(fp);
	return 0;
}

int main(int argc,char *argv[])
{
	signal(SIGINT,(void *)stop_fun);
	//键值
	key_t key1 = ftok("/home/wwz/xyd/bank/out/",1);
	key_t key2 = ftok("/home/wwz/xyd/bank/out/",2);
	//打开请求消息队列
	reqid = msgget(key1,IPC_CREAT|0644);
	if(reqid == -1){perror("msgget");return -1;}
	//打开应答消息队列
	resid = msgget(key2,IPC_CREAT|0644);
	if(resid == -1){perror("msgget");return -1;}
	
	printf("取款服务启动\n");
	while(1)
	{
		//轮循等待从队列中读出数据
		user req;
		if(msgrcv(reqid,&req,sizeof(req)-sizeof(long),3,0)==-1)
		{perror ("msgrcv");continue;}
		printf("取款帐号:%s\n",req.name);
		res r;
		user u;
		if(get(req.name,&u)==-1)
		{strcpy(r.msg,"无效账户");goto send;}
		if(strcmp(req.password,u.password))
		{strcpy(r.msg,"密码错误");goto send;}
		if(req.money>u.money)
		{sprintf(r.msg,"抱歉,余额不足,您的余额为:%f",u.money);goto send;}
		u.money -= req.money;
		if(save(&u) == -1)
		{strcpy(r.msg,"取款失败");goto send;}
		r.money = u.money;
		sprintf(r.msg,"取款成功,您的余额为:%f",u.money);
send:		
		r.type = 3;
		if(msgsnd(resid,&r,sizeof(res)-sizeof(long),0)==-1)
        {perror("msgsnd");continue;}
	}
	return 0;
}

makefile文件

shell 复制代码
all:serve client ./out/enroll ./out/save ./out/take ./out/trans
obj = $(wildcard ./src/*.c)

serve:$(obj)
	gcc  ./src/serve.c -o $@
client:$(obj)
	gcc  ./src/client.c -o $@
./out/enroll:$(obj)
	gcc  ./src/enroll.c -o $@
./out/save:$(obj)
	gcc ./src/save.c -o $@
./out/take:$(obj)
	gcc  ./src/take.c -o $@
./out/trans:$(obj)
	gcc  ./src/transfer.c -o $@

clean:
	rm serve client ./out/enroll ./out/save ./out/take ./out/trans
.PHONY:clean
相关推荐
0xDevNull13 小时前
Linux切换JDK版本详细教程
linux
进击的丸子14 小时前
虹软人脸服务器版SDK(Linux/ARM Pro)多线程调用及性能优化
linux·数据库·后端
茶杯梦轩14 小时前
从零起步学习RabbitMQ || 第二章:RabbitMQ 深入理解概念 Producer、Consumer、Exchange、Queue 与企业实战案例
服务器·后端·消息队列
Johny_Zhao2 天前
OpenClaw安装部署教程
linux·人工智能·ai·云计算·系统运维·openclaw
RuoZoe2 天前
重塑WPF辉煌?基于DirectX 12的现代.NET UI框架Jalium
c语言
初次攀爬者2 天前
Kafka 基础介绍
spring boot·kafka·消息队列
初次攀爬者3 天前
RocketMQ 消息可靠性保障与堆积处理
后端·消息队列·rocketmq
初次攀爬者3 天前
RocketMQ 集群介绍
后端·消息队列·rocketmq
初次攀爬者3 天前
RocketMQ 基础学习
后端·消息队列·rocketmq
chlk1234 天前
Linux文件权限完全图解:读懂 ls -l 和 chmod 755 背后的秘密
linux·操作系统