在学习了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.主要思路和设计定义
- 在客户端分为两个块写,第一个块写开户功能和登陆功能,再在登陆功能后写一个功能函数,进入登陆后操作功能。
- 服务端只有接受消息的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查看消息队列的信息。