基于消息队列的自助ATM

在学习了UC(UNIX环境下的标准C语言)之后,用消息队列实现ATM的系统。

主要分为三个文件写:1.菜单文件(界面文件)。2.客户端系统。3.服务器系统。拥有两个main函数。

1.菜单文件代码以及头文件

menu.h文件 主要包含了头文件

#ifndef _MENU_H__ //头文件卫士
#define _MENU_H__

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<strings.h>
#include<unistd.h>
#include<sys/stat.h>
#include<sys/msg.h>
#include<sys/ipc.h>
#include<errno.h>

void firstMenu();//第一级目录

void secondMenu();//第二季目录

#endif//_MENU_H__

menu.c文件

#include"menu.h"

//主界面
void firstMenu(){
	printf("------欢迎来到自助银行------\n");
	printf("============================\n");
	printf("          [1]开户           \n");
	printf("          [2]登录           \n");
	printf("          [3]注销           \n");
	printf("          [0]退出           \n");
	printf("============================\n");
	printf("input:");
}
//登陆界面
void secondMenu(){
	printf("------请选择需要的操作------\n");
	printf("       [1]查看个人信息      \n");
	printf("       [2]存钱              \n");
	printf("       [3]取钱              \n");
	printf("       [4]转账              \n");
	printf("       [5]修改密码        \n");
	printf("       [0]退出          \n");
	printf("============================\n");
	printf("input:");
}

2.主要思路和设计定义

  1. 在客户端分为两个块写,第一个块写开户功能和登陆功能,再在登陆功能后写一个功能函数,进入登陆后操作功能。
  2. 服务端只有接受消息的m.type功能,再接受完之后返还给客户端一个消息,所以在客户端将发送和接受分开写。

3.客户端代码

client.h文件

#ifndef _CLIENT_H__ //头文件卫士
#define _CLIENT_H__

#define MSG_LEN 128 //传递信息长度

typedef struct Inf{//信息结构体
	int id;
	int password;
	double money;
}Inf;
typedef struct Inf* Man;

typedef struct Msg{//发送结构体
	int type;
	char buf[MSG_LEN];
}Msg;

typedef struct Login{//登录结构体
	int flag; //标记
	int loginId; //登陆的ID
}Login;

typedef struct Move{//转账结构体
	int moveId; //需要转账的账号
	double moveMoney; //需要转账的金额
	int ownId; //自己的ID
}Move;

typedef struct Reveise{//修改个人信息结构体
	int rePassWord; //需要修改的密码
	int Id; //登陆账号
}Rev;

#endif//_CLIENT_H__

client.c 文件  客户端main函数也再其中

#include"menu.h"
#include"client.h"

Inf *pinf = NULL; //信息结构体指针

//建立消息队列(发送给服务器消息)
int msgSend(){ 
	key_t keySend = ftok(".",111);
	int x = msgget(keySend,0644|IPC_CREAT);
	return x;
}
//简历消息队列(接受服务器发来的消息
int msgReceive(){
	key_t keyReceive = ftok(".",222);
	int x = msgget(keyReceive,0644|IPC_CREAT);
	return x;
}

/*发送函数*****************************************************************/

//开户
void cliendSendPassToServe(int msgid,int password){//发送给服务器密码
	Msg m = {};
	m.type = 1;//开户类型
	memcpy(m.buf,&password,4);
	int ret = msgsnd(msgid,&m,strlen(m.buf),0);
		if(ret == -1){
			perror("msgsnd");	
		}	
}

//登陆
void clientSendAdminToServe(int msgid,Inf admin){//发送给服务器账号和密码
	Msg m = {};
	m.type = 2;//登录类型
	memcpy(m.buf,&admin,sizeof(Inf));
	int ret = msgsnd(msgid,&m,sizeof(Inf),0);
	if(ret == -1){
		perror("msgsnd");	
	}
}

//查看自己信息
void clientSendAdminIdToServe(int msgid,int adminInf){//发送给服务器自己的ID
	Msg m = {};
	m.type = 3;//查看个人信息
	memcpy(m.buf,&adminInf,4);
	int ret = msgsnd(msgid,&m,strlen(m.buf),0);
	if(ret == -1){
		perror("msgsnd");	
	}
}

//存钱
void clientSendMoneyToServe(int msgid,int adminInf,double num){
//发送账号和要存钱的金额
	Msg m = {};
	Inf i = {};
	i.id = adminInf;
	i.money = num;
	m.type = 4;//存钱
	memcpy(m.buf,&i,sizeof(Inf));
	int ret = msgsnd(msgid,&m,sizeof(Inf),0);
	if(ret == -1){
		perror("msgsnd");	
	}
}

//取钱
void clientSendMoney2ToServe(int msgid,int adminInf,double num){
//发送账号和要取钱的金额
	Msg m = {};
	Inf i = {};
	i.id = adminInf;
	i.money = num;
	m.type = 5;//取钱
	memcpy(m.buf,&i,sizeof(Inf));
	int ret = msgsnd(msgid,&m,sizeof(Inf),0);
	if(ret == -1){
		perror("msgsnd");	
	}
}

//转账
void clientSendMoveToServe(int msgid,int moveId,double moveMoney,int ownId){
//发送需转账的账号,转账金额和自己的账号
	Msg m = {};
	Move i = {};
	i.moveId = moveId;
	i.moveMoney = moveMoney;
	i.ownId = ownId;
	m.type = 6;//转账
	memcpy(m.buf,&i,sizeof(Inf));
	int ret = msgsnd(msgid,&m,sizeof(Inf),0);
	if(ret == -1){
		perror("msgsnd");	
	}
}

//修改密码
void clientSendNewPassToServe(int msgid,int rePassWord,int adminInf){
//发送自己的ID和要修改的密码
	Msg m = {};
	Rev r = {};
	r.rePassWord = rePassWord;
	r.Id = adminInf;
	m.type = 7;//修改密码
	memcpy(m.buf,&r,sizeof(Rev));
	int ret = msgsnd(msgid,&m,sizeof(Rev),0);
	if(ret == -1){
		perror("msgsnd");	
	}
}

//注销
void clientSendIdDelToServe(int msgid,int idDel){//发送要注销的账号
	Msg m = {};
	m.type = 8;//注销账户
	memcpy(m.buf,&idDel,4);
	int ret = msgsnd(msgid,&m,strlen(m.buf),0);
	if(ret == -1){
		perror("msgsnd");	
	}
}

//登录后的功能函数
void loginFunction(int adminInf){
	int msgidSend = msgSend();
	int msgidReceive = msgReceive();
	while(1){
		loop1:
		secondMenu();
		int opt2 = 0;
		scanf("%d",&opt2);
		//发送消息
		switch(opt2){
			case 1://查看个人信息
			clientSendAdminIdToServe(msgidSend,adminInf);
			break;

			case 2://存钱,不需要返回值
			printf("请输入要存入的金额:\n");
			double num = 0;
			scanf("%lf",&num);
			clientSendMoneyToServe(msgidSend,adminInf,num);
			printf("成功!\n");
			goto loop1;
			break;

			case 3://取钱,不需要返回
			printf("请输入要取出的金额:\n");
			double num1 = 0;
			scanf("%lf",&num1);
			clientSendMoney2ToServe(msgidSend,adminInf,num1);
			printf("成功!\n");
			goto loop1;
			break;
			
			case 4://转账
			printf("请输入要转出的金额:\n");
			double moveMoney = 0;
			scanf("%lf",&moveMoney);
			printf("请输入要转入的账户:\n");
			int moveId = 0;
			scanf("%d",&moveId);
			clientSendMoveToServe(msgidSend,moveId,moveMoney,adminInf);
			printf("成功!\n");
			goto loop1;
			break;

			case 5://修改密码
			printf("请输入要修改的新密码:\n");
			int rePassWord = 0;
			scanf("%d",&rePassWord);
			clientSendNewPassToServe(msgidSend,rePassWord,adminInf);
			printf("成功!\n");
			goto loop1;
			break;

			case 0:
			return;
		}

		//接收消息
		Msg m = {};
		int ret = msgrcv(msgidReceive,&m,MSG_LEN,0,0);
		if(ret == -1){
			perror("msgrcv");
		}
		Inf man = {};//接收信息
		Inf nowMoney = {};
		int flag1 = 0;//接收转账消息
		switch(m.type){
			case 3://接受信息
			memcpy(&man,m.buf,ret);
			printf("您的信息为:\n");
			printf("账号:%d\n密码:%d\n账户余额:%lf\n",man.id,man.password,man.money);
			break;

		}
	}	
}

int main(){
	int msgidSend = msgSend();
	int msgidReceive = msgReceive();
	while(1){
		loop2:
		firstMenu();
		int opt = 0;
		scanf("%d",&opt);
		//发送消息
		switch(opt){
			case 1://开户
			printf("请输入您要设置的6位数密码:");
			int passwordOpen = 0;
			scanf("%d",&passwordOpen);
			cliendSendPassToServe(msgidSend,passwordOpen);
			break;

			case 2://登录
			printf("请输入您的账号:\n");
			int idAdmin = 0;
			scanf("%d",&idAdmin);
			printf("请输入您的密码:\n");
			int passwordAdmin = 0;
			scanf("%d",&passwordAdmin);
			Inf admin = {};//用一个结构体存储要传输的密码和账号
			admin.id = idAdmin;
			admin.password = passwordAdmin;
			clientSendAdminToServe(msgidSend,admin);
			break;
			
			case 3://注销
			printf("请输入您的账号:\n");
			int idDel = 0;
			scanf("%d",&idDel);
			clientSendIdDelToServe(msgidSend,idDel);
			goto loop2;
			break;

			case 0://退出
			printf("Bye bye!\n");
			int a = msgctl(msgidSend,IPC_RMID,NULL);
			if(a == -1){
				perror("msgctl");	
			}
			int b = msgctl(msgidReceive,IPC_RMID,NULL);
			if(b == -1){
				perror("msgctl");	
			}
			exit(0);
		}

		//接收消息
		Msg m = {};
		int ret = msgrcv(msgidReceive,&m,MSG_LEN,0,0);
		if(ret == -1){
			perror("msgrcv");	
		}
		Inf open = {};
		Login admin = {};
		switch(m.type){
			case 1://开户类型
			memcpy(&open,m.buf,ret);
			printf("开户成功!\n");
			printf("您的开户信息为:\n");
			printf("账号:%d\n密码:%d\n账户余额:%lf\n",open.id,open.password,open.money);
			break;


			case 2://登录类型
			memcpy(&admin,m.buf,ret);
			if(admin.flag == 1){
				printf("登录成功!\n");
				int adminInf = admin.loginId;
				loginFunction(adminInf);
			}else{
				printf("登录失败!\n");	
			}
			break;
		}
	}	

	return 0;	
}

1.注意接受消息和发送消息的模块。

2.在接受登陆成功消息之后,跳转至登陆后功能函数

3.服务器代码

serve.h 文件 (大概结构体和客户端都一样)

#ifndef _SERVE_H__ //头文件卫士
#define _SERVE_H__

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<string.h>
#include<strings.h>
#include<unistd.h>
#include<sys/stat.h>
#include<sys/msg.h>
#include<sys/ipc.h>
#include<errno.h>
#include<fcntl.h>
#define MSG_LEN 128

typedef struct Inf{//信息结构体
	int id;
	int password;
	double money;
}Inf;
typedef struct Inf* Man;

typedef struct Msg{//发送结构体
	int type;
	char buf[MSG_LEN];
}Msg;

typedef struct Login{//登录结构体
	int flag;
	int loginId;
}Login;

typedef struct Move{//转账结构体
	int moveId;
	double moveMoney;
	int ownId;
}Move;

typedef struct Reveise{//修改密码结构体
	int rePassWord;
	int Id;
}Rev;

#endif//_SERVE_H__

serve.c 文件 main

#include"serve.h"
#include"menu.h"
/*
typedef struct Inf{
	int id;
	int password;
	double money;
}Inf;
typedef struct Inf* Man;

typedef struct Msg{
	int type;
	char buf[MSG_LEN];
}Msg;
*/
Inf *pinf = NULL;
size_t cnt = 100;//动态内存总容量
size_t cntOfInf = 0;//动态内存中信息的个数

int msgReceive(){ //接受客户端的消息
	key_t keyReceive = ftok(".",111);
	int x = msgget(keyReceive,0644|IPC_CREAT);
	if(x == -1){
		perror("msgget");
		return -1;
	}
	return x;
}

int msgSend(){ //发送给客户端消息
	key_t keySend = ftok(".",222);
	if(keySend == -1){
		perror("ftok");
		return -1;
	}
	int x = msgget(keySend,0644|IPC_CREAT);
	if(x == -1){
		perror("msgget");
		return -1;
	}
	return x;
}
/*功能函数*******************************************************************/

int addInf(Inf i){//在服务器的动态内存增加用户
	if(pinf == NULL){
		pinf = calloc(cnt,sizeof(Inf));
		if(pinf == NULL){
			return -1;	
		}
	}
	pinf[cntOfInf] = i;
	cntOfInf++;
	return 0;
}

void showInf(Inf* pinf){ //展示信息
	printf("账号:%d  密码:%d  余额:%lf\n",pinf->id,pinf->password,pinf->money);	
}

void listInf(){//服务器展开所有用户信息
	if(cntOfInf == 0){
		printf("没有用户信息!\n");	
	}else{
		for(int i=0;i<cntOfInf;i++){
			showInf(pinf+i);	
		}	
	}
}

int findInf(Inf f[],int id){//查找
	for(int i=0;i<cntOfInf;i++){
		if(f[i].id == id){
			return i;	
		}	
	}
	return -1;
}

void delInf(Inf f[],int idDel){ //删除信息
	f[idDel] = f[cntOfInf-1];
	--cntOfInf;
}
/*发送函数*******************************************************************/

//开户,返还信息结构体
void serveSendInfToClient(int msgid,int password){ 
	srand(time(NULL));
	Inf inf = {};
	inf.id = rand()%1000+19000;
	inf.password = password;
	inf.money = 0;
	addInf(inf);//将信息添加至动态内存
	Msg m = {};
	m.type = 1;//开户接收
	memcpy(m.buf,&inf,sizeof(Inf));
	int ret = msgsnd(msgid,&m,sizeof(Inf),0);//发送数据给客户端
	if(ret == -1){
		perror("msgsnd");	
	}
}

//登陆,返还登陆人的ID和标记
void sendAdminToClient(int msgid,int flag,int loginId){
	Msg m = {};
	Login l = {};
	m.type = 2;//登录接收
	l.flag = flag;
	l.loginId = loginId;
	memcpy(m.buf,&l,sizeof(Login));
	int ret = msgsnd(msgid,&m,sizeof(Login),0);
	if(ret == -1){
		perror("msgsnd");	
	}
}

//查看信息,返还信息给服务器
void sendFindInfToClient(int msgid,Inf i){
	Msg m = {};
	m.type = 3;//查看自己信息
	memcpy(m.buf,&i,sizeof(Inf));
	int ret = msgsnd(msgid,&m,sizeof(Inf),0);
	if(ret == -1){
		perror("msgsnd");	
	}
}

//加载信息
int load(const char* path){
	FILE *fb = fopen(path,"r");
	if(fb == NULL){
		return -1;	
	}
	Inf i = {};
	while(fread(&i,sizeof(Inf),1,fb)>0){
		addInf(i);	
	}
	fclose(fb);
	return 0;
}

//存储信息
int save(const char *path){
	FILE* fb = fopen(path,"w");
	if(fb == NULL){
		return -1;	
	}
	for(int i=0;i<cntOfInf;i++){
		fwrite(pinf+i,sizeof(Inf),1,fb);	
	}
	fclose(fb);
	return 0;
}

int main(){
	int msgidReceive = msgReceive();
	int msgidSend = msgSend();
	load("inf.txt");
	while(1){
		Msg m = {};//用于接收信息的结构体变量
		int ret = msgrcv(msgidReceive,&m,MSG_LEN,0,0);
		int passwordOpen = 0;//用于接收开户密码
		int flag = 0;
		int loginId = 0;
		Inf admin = {};//用于接收登录账号和密码
		int adminInf = 0;//记录要查询人的id
		Inf bal = {};//存储要存的钱和用户id
		Inf bal2 = {};//存储要取的钱和用户id
		Inf move = {};//存储转账的金额和账户
		Move q = {};//存储转账信息
		Rev r = {};//存储新密码
		int idDel = 0;//存储要删除的账号
		switch(m.type){
			case 1://得到开户的密码
			memcpy(&passwordOpen,m.buf,ret);
			printf("接收到信号!\n");
			printf("得到密码:%d\n",passwordOpen);
			serveSendInfToClient(msgidSend,passwordOpen);//发送用户信息给服务器
			printf("=====================\n");
			printf("现有的用户信息:\n");
			listInf();
			printf("=====================\n");
			save("inf.txt");
			break;

			case 2://得到登录传来的密码,返还标记结构体
			memcpy(&admin,m.buf,ret);
			loginId = admin.id;
			printf("接收到登录信号!\n");
			printf("得到的登录账号:%d\n",admin.id);
			printf("得到的登录密码:%d\n",admin.password);
			printf("=====================\n");
			int index = findInf(pinf,admin.id);
			if(index != -1 && pinf[index].password == admin.password){
				flag = 1;
			}else{
				flag = 0;	
			}
			printf("标记为:%d\n",flag);
			sendAdminToClient(msgidSend,flag,loginId);
			printf("标记已发送!\n");
			save("inf.txt");
			break;

			case 3://得到查看信息人的id,返还信息结构体
			memcpy(&adminInf,m.buf,ret);
			printf("信息ID:%d\n",adminInf);
			int man = findInf(pinf,adminInf);//信息人下标
			
			Inf manInf = {};
			manInf.password = pinf[man].password;
			manInf.id = pinf[man].id;
			manInf.money = pinf[man].money;
			printf("余额2:%lf\n",pinf[man].money);
			sendFindInfToClient(msgidSend,manInf);
			printf("信息已发送!\n");
			save("inf.txt");
			break;

			case 4://存钱
			memcpy(&bal,m.buf,ret);
			printf("信息ID:%d\n",bal.id);
			printf("需要存储金额:%lf\n",bal.money);
			man = findInf(pinf,bal.id);
			pinf[man].money += bal.money;
			printf("现有金额:%lf\n",pinf[man].money);
			printf("存钱成功!\n");
			printf("==========================\n");
			save("inf.txt");
			break;

			case 5://取钱
			memcpy(&bal2,m.buf,ret);
			printf("信息ID:%d\n",bal2.id);
			printf("需要取出金额:%lf\n",bal2.money);
			man = findInf(pinf,bal2.id);
			pinf[man].money -= bal2.money;
			printf("现有金额:%lf\n",pinf[man].money);
			printf("取钱成功!\n");
			printf("==========================\n");
			save("inf.txt");
			break;

			case 6://转账,返还余额
			memcpy(&q,m.buf,ret);
			printf("需要转到的账户ID:%d\n",q.moveId);
			printf("需要转账的金额:%lf\n",q.moveMoney);
			printf("信息ID:%d\n",q.ownId);
			man = findInf(pinf,q.moveId);//找到需要转账的ID
			pinf[man].money += q.moveMoney;
			int man2 = findInf(pinf,q.ownId);
			pinf[man2].money -= q.moveMoney;
			save("inf.txt");
			break;

			case 7://修改密码
			memcpy(&r,m.buf,ret);
			printf("需要修改的新密码为:%d\n",r.rePassWord);
			man = findInf(pinf,r.Id);
			pinf[man].password = r.rePassWord;
			printf("修改密码成功!\n");
			save("inf.txt");
			break;

			case 8://注销
			memcpy(&idDel,m.buf,ret);
			printf("需要注销的账号为:%d\n",idDel);
			man = findInf(pinf,idDel);
			delInf(pinf,man);
			listInf();
			save("inf.txt");
			break;
		}
	}


	return 0;	
}

服务器和客户端不一样,只用接受信息,在接受信息的同时发送出去,没有功能操作。

4.总结

在服务器和客户端的发送信息一定搞要对接清楚,不然会出现信息传递失败,并且信息残留在消息队列的情况,在程序运行结束时,用 kill -2 [服务器的进程编号] 的命令结束服务器程序,并且用 ipcs -q查看消息队列的信息。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值