Linux系统编程阶段项目:基于消息队列的银行管理系统
Linux系统编程阶段项目:基于消息队列的银行管理系统。
一、实现的功能:(1)开户(2)存款(3)取款(4)查询(5)转账(6)销户(7)退出
二、用到的知识点:
文件操作
循环创建子进程
进程通信----消息队列
模块编程
三、进程任务分解
可以将进程分为客户端进程和服务端进程,分别的任务有:
服务端进程:
1) 首先用msgget函数创建两个消息队列
消息队列1:作为请求消息队列,存放客户端进程发来的各种请求,服务端读;
注意:根据操作的不同,会发送不同类型的消息,可以自己定义消息的类型,这里按照在客户端菜单写的顺序,开户 --1类消息;存款 -- 2类消息;取款 -- 3类消息;查询 -- 4类消息;转账 -- 5类消息;销户 -- 6类消息。
消息队列2:作为应答消息队列,存放服务端进程回复给客户端进程的消息,客户端读;
2)利用vfork函数循环创建进程
父进程阻塞就行,子进程通过exec函数来调用函数功能模块的可执行文件
for(i=0; i<sizeof(g_srv)/sizeof(g_srv[0]); i++)
{
//g_srv[i].srv_pid = vfork();//循环创建子进程
if((g_srv[i].srv_pid = vfork()) == -1)
{
perror("vfork");
return -1;
}
if(g_srv[i].srv_pid == 0)
{
if(execl(g_srv[i].srv_path,g_srv[i].srv_path,NULL)==-1)
{
perror("execl");
return -1;
}
return 0;
}
3)捕获用户发来的Ctrl+c的信号,结束进程 并删除消息队列
通过exit函数和msgctl功能函数实现
if(msgctl(g_reqid,IPC_RMID,NULL) == -1)
{
perror("msgctl");
}
else
{
printf("销毁请求消息队列成功!\n");
}
if(msgctl(g_resid,IPC_RMID,NULL) == -1)
{
perror("msgctl");
}
else
{
printf("销毁应答消息队列成功!\n");
}
总的服务端代码如下所示:
#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>
#define PATH_MAX 1000
static int g_reqid = -1; //作为请求的消息队列的返回id
static int g_resid = -1; //作为响应的消息队列的返回id
typedef struct tag_Service{
char srv_path[PATH_MAX+1];
pid_t srv_pid;
}srv; //定义一个服务端结构体
static srv g_srv[]={ //结构体数组
{"./open",-1}, //开户
{"./close1",-1}, //销户
{"./save",-1}, //存款
{"./withdraw",-1}, //取款
{"./query",-1}, //查询
{"./trans",-1}, //转账
};
int Init_msg(); //初始化两个消息队列
int start();
void deInit_msg();
void sigint(int signum);
int stop();
int main()
{
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 kid1,kid2;
kid1 = ftok("/home/whs/002/0706",1); //键值
kid2 = ftok("/home/whs/002/0706",2);
g_reqid = msgget(kid1,IPC_CREAT|0644); //创建消息队列1
if(g_reqid== -1)
{
perror("msgget");
return -1;
}
printf("创建请求消息队列成功!\n");
g_resid =msgget(kid2,IPC_CREAT|0644); //创建消息队列2
if(g_resid == -1)
{
perror("msgget");
return -1;
}
printf("创建应答消息队列成功!\n");
return 0;
}
int start()
{
printf("启动服务.....\n");
size_t i;
for(i=0; i<sizeof(g_srv)/sizeof(g_srv[0]); i++)
{
//g_srv[i].srv_pid = vfork();//循环创建子进程
if((g_srv[i].srv_pid = vfork()) == -1)
{
perror("vfork");
return -1;
}
if(g_srv[i].srv_pid == 0)
{
if(execl(g_srv[i].srv_path,g_srv[i].srv_path,NULL)==-1)
{
perror("execl");
return -1;
}
return 0;
}
}
return 0;
}
void deInit_msg()
{
printf("服务器杀死.....\n");
if(msgctl(g_reqid,IPC_RMID,NULL) == -1)
{
perror("msgctl");
}
else
{
printf("销毁请求消息队列成功!\n");
}
if(msgctl(g_resid,IPC_RMID,NULL) == -1)
{
perror("msgctl");
}
else
{
printf("销毁应答消息队列成功!\n");
}
//return ;
}
void sigint(int signum)
{
printf("%d\n",signum);
stop();
exit(0);
}
int stop()
{
printf("停止所有业务!\n");
size_t i;
for(i=0; i<sizeof(g_srv)/sizeof(g_srv[0]); i++)
{
if(kill(g_srv[i].srv_pid,SIGINT) == -1) //SIGINT 2号信号 ctrl+c
{
perror("kill");
return -1;
}
}
for(;;)
{
if(wait(NULL) == -1)
{
perror("wait");
break;
}
}
return 0;
}
模块功能编写:
1)开户操作
成功创建文件后将客户端进程发来的用户信息写入文件,返回成功的消息类型给客户端
#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 g_reqid; //作为请求的消息队列的返回id
int g_resid;
void myfun(int num)
{
printf ("开户服务:即将停止。\n");
exit(0);
}
typedef struct msgbuf{ //从请求队列接收
long mtype; //消息类型
char id[100]; //身份证号
char name[256]; //用户名
char pw[9]; //密码
float money; //银行卡余额
}userm;
typedef struct tag_OpenRespond{//往应答消息队列发送
long mtype;
char id[100]; //身份证号
char msg[256]; //写入的消息,成功或者失败
}OPEN_RESPOND;
typedef struct tag_Account {//把从结构体读到的数据存储
char id[100];
char name[256];
char pw[9];
float money; //银行卡余额
}ACCOUNT;
void sigint (int signum);
int save (const ACCOUNT* acc);
int main()
{
signal(SIGINT,myfun);
key_t kid1,kid2;
int pf,pd;
char buf1[100];
kid1 = ftok("/home/whs/002/0706",1); //请求消息队列键值
kid2 = ftok("/home/whs/002/0706",2); //应答消息队列键值
g_reqid = msgget(kid1,IPC_CREAT|0644); //打开请求消息队列
if(g_reqid== -1)
{
perror("msgget");
return -1;
}
g_resid =msgget(kid2,IPC_CREAT|0644); //打开应答消息队列
if(g_resid == -1)
{
perror("msgget");
return -1;
}
printf("现在开始开户业务......\n");
//读请求消息队列
for(;;)
{
userm req;
if(msgrcv(g_reqid,&req,sizeof(req)-sizeof(req.mtype),1,0)== -1)
{
perror ("msgrcv");
continue;
}
printf("用户帐号:");
printf("%s\n",req.id);
OPEN_RESPOND res;
strcpy(res.id,req.id);
ACCOUNT acc;
memset(buf1,0,sizeof(buf1));
sprintf(buf1,"./%s.txt",req.id);
pf = open(buf1,O_WRONLY);
if(pf > 0)
{
//往应答消息队列里写入
res.mtype = 1;
sprintf(res.msg,"已有该用户,创立账户失败!");
msgsnd(g_resid,&res,sizeof(res)-sizeof(long),0);
}
else
{
strcpy(acc.id,req.id);
strcpy(acc.name,req.name);
strcpy(acc.pw,req.pw);
acc.money = req.money;
save(&acc);//数据保存函数
//往应答消息队列里写入
res.mtype = 1;
sprintf(res.msg,"创立账户成功!");
msgsnd(g_resid,&res,sizeof(res)-sizeof(long),0);
}
}
close(pf);
return 0;
}
void sigint (int signum)
{
printf ("开户服务:即将停止。\n");
exit (0);
}
//保存数据
int save (const ACCOUNT *acc)
{
char pathname[256];
memset(pathname,0,sizeof(pathname));
sprintf(pathname,"%s.txt",acc->id);
//printf("%s\n",pathname);
int fd = open(pathname,O_WRONLY|O_CREAT,0644);
if(fd == -1)
{
perror("open");
return -1;
}
if(write(fd,acc,sizeof(*acc))== -1)
{
perror ("write");
return -1;
}
close (fd);
return 0;
}
2)存款操作
根据用户信息找到对应的文件,将文件内的关于金额的值加上相应的取款金额
#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 g_reqid; //作为请求的消息队列的返回id
int g_resid; //作为应答的消息队列的返回id
typedef struct msgbuf{ //从请求队列读取的结构体内容存到这里
long mtype; //消息类型
char id[100]; //身份证号
char name[256]; //用户名
char pw[9]; //密码
float money; //银行卡余额
}userm;
typedef struct tag_OpenRespond{//写入应答消息队列信息,让客户端读
long mtype;
char id[100]; //身份证号
char msg[256]; //写入的消息,成功或者失败
float money;
}SAVE_RESPOND;
typedef struct tag_Account {//把从结构体读到的数据存储到这个结构体里面
char id[100];
char name[256];
char pw[9];
float money; //银行卡余额
}ACCOUNT;
void myfun(int num)
{
printf("存款服务即将停止!\n");
exit(0);
}
int updata(const ACCOUNT *acc); //更新数据
int get(char id[100],ACCOUNT *acc);//读取文件
int save (const ACCOUNT *acc); //保存数据
int main()
{
signal(2,myfun);
key_t kid1,kid2;
//char arr[1024];
kid1 = ftok("/home/whs/002/0706",1); //请求消息队列键值
kid2 = ftok("/home/whs/002/0706",2); //应答消息队列键值
g_reqid = msgget(kid1,IPC_CREAT|0644); //打开请求消息队列
if(g_reqid== -1) //判断打开是否失败
{
perror("msgget");
return -1;
}
g_resid =msgget(kid2,IPC_CREAT|0644); //打开应答消息队列
if(g_resid == -1)
{
perror("msgget");
return -1;
}
printf("现在开始存款业务......\n");
//读请求消息队列
while(1)
{
userm req;
if(msgrcv(g_reqid,&req,sizeof(req),2,0)== -1) //存款,2类信号
{
perror ("msgrcv");
continue;
}
printf("用户帐号:");
printf("%s\n",req.id);
SAVE_RESPOND res;
ACCOUNT acc;
if(get(req.id,&acc) == -1)//是否存在该账户
{
sprintf(res.msg,"%s无效账户",req.id);
goto send;
}
if(strcmp(req.name,acc.name))//比较用户名是否正确
{
sprintf(res.msg,"%s用户名错误",req.id);
goto send;
}
if(strcmp(req.pw,acc.pw)) //比较密码是否正确
{
sprintf(res.msg,"%s密码错误",req.id);
goto send;
}
acc.money += req.money; //余额进行存款操作
if(updata(&acc) == -1)
{
sprintf(res.msg,"%s存款失败",req.id);
goto send;
}
res.money = acc.money;
save(&acc);//数据保存函数
sprintf(res.msg,"%s存款成功,余额为:%f",req.id,res.money);
send:
res.mtype = 2; //存款,2类消息
//往应答消息队列发送应答消息
if(msgsnd(g_resid,&res,sizeof(res)-sizeof(long),0)==-1)
{
perror("msgsnd");
continue;
}
}
return 0;
}
int updata(const ACCOUNT *acc) //更新数据
{
char path[256];
sprintf(path,"%s.txt",acc->id);
int fd =open(path,O_WRONLY);
if(fd == -1)
{
perror("open");
return -1;
}
if(write(fd,acc,sizeof(*acc))==-1)
{
perror("write");
return -1;
}
close(fd);
return 0;
}
int get(char id[100],ACCOUNT *acc) //读取文件
{
char pathname[256];
sprintf(pathname,"%s.txt",id);
int fd =open(pathname,O_RDONLY);
if(fd == -1)
{
perror("open");
return -1;
}
if(read(fd,acc,sizeof(*acc))==-1)
{
perror("read");
return -1;
}
close(fd);
return 0;
}
//保存数据
int save (const ACCOUNT *acc)
{
char pathname[256];
memset(pathname,0,sizeof(pathname));
sprintf(pathname,"%s.txt",acc->id);
//printf("%s\n",pathname);
int fd = open(pathname,O_WRONLY|O_CREAT,0644);
if(fd == -1)
{
perror("open");
return -1;
}
if(write(fd,acc,sizeof(*acc))== -1)
{
perror ("write");
return -1;
}
close (fd);
return 0;
}
3)取款操作
根据用户信息找到对应的文件,将文件内的关于金额的值减去相应的取款金额
#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 g_reqid; //作为请求的消息队列的返回id
int g_resid; //作为应答的消息队列的返回id
typedef struct msgbuf{ //从请求队列读取
long mtype; //消息类型
char id[100]; //身份证号
char name[256]; //用户名
char pw[9]; //密码
float money; //银行卡余额
}userm;
typedef struct tag_OpenRespond{//写入应答消息队列信息
long mtype;
char id[100]; //身份证号
char msg[256]; //写入的消息,成功或者失败
float money;
}WITHDRAW_RESPOND;
typedef struct tag_Account {//把从结构体读到的数据存储
char id[100];
char name[256];
char pw[9];
float money; //银行卡余额
}ACCOUNT;
void myfun(int num)
{
printf("取款服务即将停止!\n");
exit(0);
}
int updata(const ACCOUNT *acc); //更新数据
int get(char id[100],ACCOUNT *acc);//读取文件
int save (const ACCOUNT *acc); //保存数据
int main()
{
signal(2,myfun);
key_t kid1,kid2;
//char arr[1024];
kid1 = ftok("/home/whs/002/0706",1); //请求消息队列键值
kid2 = ftok("/home/whs/002/0706",2); //应答消息队列键值
g_reqid = msgget(kid1,IPC_CREAT|0644); //打开请求消息队列
if(g_reqid== -1) //判断打开是否失败
{
perror("msgget");
return -1;
}
g_resid =msgget(kid2,IPC_CREAT|0644); //打开应答消息队列
if(g_resid == -1)
{
perror("msgget");
return -1;
}
printf("现在开始取款业务......\n");
//读请求消息队列
while(1)
{
userm req;
if(msgrcv(g_reqid,&req,sizeof(req),3,0)== -1) //取款,3类信号
{
perror ("msgrcv");
continue;
}
printf("用户帐号:");
printf("%s\n",req.id);
WITHDRAW_RESPOND res;
ACCOUNT acc;
if(get(req.id,&acc) == -1)//是否存在该账户
{
sprintf(res.msg,"%s无效账户",req.id);
goto send;
}
if(strcmp(req.name,acc.name))//比较用户名是否正确
{
sprintf(res.msg,"%s用户名错误",req.id);
goto send;
}
if(strcmp(req.pw,acc.pw)) //比较密码是否正确
{
sprintf(res.msg,"%s密码错误",req.id);
goto send;
}
if(acc.money < req.money) //比较取款是否大于余额
{
sprintf(res.msg,"%s抱歉,余额不足,您的余额为:%f",req.id,acc.money);
goto send;
}
acc.money -= req.money; //余额进行取款操作
if(updata(&acc) == -1)
{
sprintf(res.msg,"%s取款失败",req.id);
goto send;
}
res.money = acc.money;
save(&acc);//数据保存函数
sprintf(res.msg,"%s取款成功,余额为:%f",req.id,res.money);
send:
res.mtype = 3; //取款,3类消息
//往应答消息队列发送应答消息
if(msgsnd(g_resid,&res,sizeof(res)-sizeof(long),0)==-1)
{
perror("msgsnd");
continue;
}
}
return 0;
}
int updata(const ACCOUNT *acc) //更新数据
{
char path[256];
sprintf(path,"%s.txt",acc->id);
int fd =open(path,O_WRONLY);
if(fd == -1)
{
perror("open");
return -1;
}
if(write(fd,acc,sizeof(*acc))==-1)
{
perror("write");
return -1;
}
close(fd);
return 0;
}
int get(char id[100],ACCOUNT *acc) //读取文件
{
char pathname[256];
sprintf(pathname,"%s.txt",id);
int fd =open(pathname,O_RDONLY);
if(fd == -1)
{
perror("open");
return -1;
}
if(read(fd,acc,sizeof(*acc))==-1)
{
perror("read");
return -1;
}
close(fd);
return 0;
}
//保存数据
int save (const ACCOUNT *acc)
{
char pathname[256];
memset(pathname,0,sizeof(pathname));
sprintf(pathname,"%s.txt",acc->id);
//printf("%s\n",pathname);
int fd = open(pathname,O_WRONLY|O_CREAT,0644);
if(fd == -1)
{
perror("open");
return -1;
}
if(write(fd,acc,sizeof(*acc))== -1)
{
perror ("write");
return -1;
}
close (fd);
return 0;
}
4)查询操作
#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 g_reqid; //作为请求的消息队列的返回id
int g_resid; //作为应答的消息队列的返回id
typedef struct msgbuf{ //从请求队列读取
long mtype; //消息类型
char id[100]; //身份证号
char name[256]; //用户名
char pw[9]; //密码
float money; //银行卡余额
}userm;
typedef struct tag_OpenRespond{//写入应答消息队列信息
long mtype;
char id[100]; //身份证号
char msg[256]; //写入的消息,成功或者失败
float money;
}QUERY_RESPOND;
typedef struct tag_Account {//把从结构体读到的数据存储
char id[100];
char name[256];
char pw[9];
float money; //银行卡余额
}ACCOUNT;
void myfun(int num)
{
printf("查询服务即将停止!\n");
exit(0);
}
int get(char id[100],ACCOUNT *acc);//读取文件
int main()
{
signal(2,myfun);
key_t kid1,kid2;
//char arr[1024];
kid1 = ftok("/home/whs/002/0706",1); //请求消息队列键值
kid2 = ftok("/home/whs/002/0706",2); //应答消息队列键值
g_reqid = msgget(kid1,IPC_CREAT|0644); //打开请求消息队列
if(g_reqid== -1) //判断打开是否失败
{
perror("msgget");
return -1;
}
g_resid =msgget(kid2,IPC_CREAT|0644); //打开应答消息队列
if(g_resid == -1)
{
perror("msgget");
return -1;
}
printf("现在开始查询业务......\n");
//读请求消息队列
while(1)
{
userm req;
if(msgrcv(g_reqid,&req,sizeof(req),4,0)== -1) //查询,4类信号
{
perror ("msgrcv");
continue;
}
printf("用户帐号:");
printf("%s\n",req.id);
QUERY_RESPOND res;
ACCOUNT acc;
if(get(req.id,&acc) == -1) //是否存在该账户
{
sprintf(res.msg,"%s无效账户",req.id);
goto send;
}
if(strcmp(req.name,acc.name))//比较用户名是否正确
{
sprintf(res.msg,"%s用户名错误",req.id);
goto send;
}
if(strcmp(req.pw,acc.pw)) //比较密码是否正确
{
sprintf(res.msg,"%s密码错误",req.id);
goto send;
}
res.money = acc.money;
//save(&acc);//数据保存函数
sprintf(res.msg,"查询成功,您的余额为:%f",res.money);
send:
res.mtype = 4; //查询,4类消息
//往应答消息队列发送应答消息
if(msgsnd(g_resid,&res,sizeof(res)-sizeof(long),0)==-1)
{
perror("msgsnd");
continue;
}
}
return 0;
}
int get(char id[100],ACCOUNT *acc) //读取文件
{
char pathname[256];
sprintf(pathname,"%s.txt",id);
int fd =open(pathname,O_RDONLY);
if(fd == -1)
{
perror("open");
return -1;
}
if(read(fd,acc,sizeof(*acc))==-1)
{
perror("read");
return -1;
}
close(fd);
return 0;
}
5)转账操作
转账操作需要输入两个先关的用户信息,之后就是取款操作和存款操作的结合
#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
void myfun(int num)
{
printf("转账服务即将停止!\n");
exit(0);
}
int g_reqid;
int g_resid;
typedef struct msgbuf{
long mtype; //消息类型
char id[100]; //身份证号
char name[256]; //用户名
char pw[9]; //密码
float money; //银行卡余额
}userm; //用这个结构体接收请求队列传过来的数据
typedef struct tag_Account {
char id[100];
char name[256];
char pw[9];
float money; //银行卡余额
}ACCOUNT; //把结构体里面的数据存到这里面
typedef struct tag_OpenRespond{
long mtype;
char id[100]; //身份证号
char msg[256]; //写入的消息,成功或者失败
float money;
}TRANS_RESPOND; //写入应答消息队列信息,让客户端读
int updata(const ACCOUNT *acc); //更新数据
int get(char id[100],ACCOUNT *acc);
int save (const ACCOUNT *acc); //保存数据
int main()
{
//信号注销函数,接收到信号之后杀死自己
signal(SIGINT,myfun);
//ftok()获取键值
key_t kid1,kid2;
kid1 = ftok("/home/whs/002/0706",1); //请求消息队列键值
kid2 = ftok("/home/whs/002/0706",2); //应答消息队列键值
//获取请求的消息队列
g_reqid = msgget(kid1,IPC_CREAT|0644);
if(g_reqid== -1)
{
perror("msgget");
return -1;
}
//获取应答消息队列
g_resid =msgget(kid2,IPC_CREAT|0644);
if(g_resid == -1)
{
perror("msgget");
return -1;
}
printf("现在开始转账业务......\n");
while(1) //死循环,接收请求队列发送过来的消息
{
userm req1; //存转账A用户
userm req2; //存要被转账的B用户
//读请求消息队列
if(msgrcv(g_reqid,&req1,sizeof(req1),5,0)== -1) //转账用,5类信号
{
perror("msgrcv");
continue;
}
if(msgrcv(g_reqid,&req2,sizeof(req2),55,0)== -1) //转账用,55类信号
{
perror("msgrcv");
continue;
}
printf("转账用户:");
printf("%s\n",req1.id);
printf("被转账用户:");
printf("%s\n",req2.id);
ACCOUNT acc1,acc2;
TRANS_RESPOND res1,res2;
if(get(req1.id,&acc1) == -1)//是否存在要转账的用户
{
sprintf(res1.msg,"%s无效帐号",req1.id);
goto send;
}
if(get(req2.id,&acc2) == -1)//是否存在被转账的用户
{
sprintf(res2.msg,"%s无效帐号",req2.id);
goto whs;
}
if(strcmp(req1.name,acc1.name))//比较转账用户的用户名是否正确
{
sprintf(res1.msg,"%s用户名错误",req1.id);
goto send;
}
if(strcmp(req1.pw,acc1.pw)) //比较转账用户的密码是否正确
{
sprintf(res1.msg,"%s密码错误",req1.id);
goto send;
}
if(acc1.money < req1.money) //比较转账用户的转款是否大于余额
{
sprintf(res1.msg,"%s抱歉,余额不足,您的余额为:%f",req1.id,acc1.money);
goto send;
}
acc1.money -= req1.money; //转账用户进行取款操作
acc2.money += req1.money; //被转账用户进行存款操作
if(updata(&acc1) == -1)
{
sprintf(res1.msg,"%s取款失败",req1.id);
goto send;
}
if(updata(&acc2) == -1)
{
sprintf(res2.msg,"%s存款失败",req2.id);
goto whs;
}
save(&acc1);//保存转账用户数据
save(&acc2);//保存被转账用户数据
res1.money = acc1.money;
res2.money = acc2.money;
sprintf(res1.msg,"%s取款成功,您当前余额为:%f",req1.id,res1.money);
send:
res1.mtype = 5;
if(msgsnd(g_resid,&res1,sizeof(res1)-sizeof(long),0)==-1)
{
perror("perror");
continue;
}
sprintf(res2.msg,"%s存款成功,被转账用户当前余额为:%f",req2.id,res2.money);
whs:
res2.mtype = 55;
if(msgsnd(g_resid,&res2,sizeof(res1)-sizeof(long),0)==-1)
{
perror("perror");
continue;
}
}
return 0;
}
int updata(const ACCOUNT *acc) //更新数据
{
char path[256];
sprintf(path,"%s.txt",acc->id);
int fd =open(path,O_WRONLY);
if(fd == -1)
{
perror("open");
return -1;
}
if(write(fd,acc,sizeof(*acc))==-1)
{
perror("write");
return -1;
}
close(fd);
return 0;
}
int get(char id[100],ACCOUNT *acc) //读取文件
{
char pathname[256];
sprintf(pathname,"%s.txt",id);
int fd =open(pathname,O_RDONLY);
if(fd == -1)
{
perror("open");
return -1;
}
if(read(fd,acc,sizeof(*acc))==-1)
{
perror("read");
return -1;
}
close(fd);
return 0;
}
//保存数据
int save (const ACCOUNT *acc)
{
char pathname[256];
memset(pathname,0,sizeof(pathname));
sprintf(pathname,"%s.txt",acc->id);
//printf("%s\n",pathname);
int fd = open(pathname,O_WRONLY|O_CREAT,0644);
if(fd == -1)
{
perror("open");
return -1;
}
if(write(fd,acc,sizeof(*acc))== -1)
{
perror ("write");
return -1;
}
close (fd);
return 0;
}
6)注销账户操作
根据提示信息,找到相对应的文件,先要情况账户中的余额才能注销账户
#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
void myfun(int num)
{
printf("销户服务即将停止!\n");
exit(0);
}
int g_reqid;
int g_resid;
typedef struct msgbuf{
long mtype; //消息类型
char id[100]; //身份证号
char name[256]; //用户名
char pw[9]; //密码
float money; //银行卡余额
}userm; //用这个结构体接收请求队列传过来的数据
typedef struct tag_Account {
char id[100];
char name[256];
char pw[9];
float money; //银行卡余额
}ACCOUNT; //把结构体里面的数据存到这里面
typedef struct tag_OpenRespond{
long mtype;
char id[100]; //身份证号
char msg[256]; //写入的消息,成功或者失败
float money;
}CLOSE_RESPOND; //写入应答消息队列信息,让客户端读
int delete(char id[100]);
int get(char id[100],ACCOUNT *acc);
int main()
{
//信号注销函数,接收到信号之后杀死自己
signal(SIGINT,myfun);
//ftok()获取键值
key_t kid1,kid2;
kid1 = ftok("/home/whs/002/0706",1); //请求消息队列键值
kid2 = ftok("/home/whs/002/0706",2); //应答消息队列键值
//获取请求的消息队列
g_reqid = msgget(kid1,IPC_CREAT|0644);
if(g_reqid== -1)
{
perror("msgget");
return -1;
}
//获取应答消息队列
g_resid =msgget(kid2,IPC_CREAT|0644);
if(g_resid == -1)
{
perror("msgget");
return -1;
}
printf("现在开始销户业务......\n");
while(1) //死循环,接收请求队列发送过来的消息
{
userm req;
//读请求消息队列
if(msgrcv(g_reqid,&req,sizeof(req),6,0)== -1)
{
perror("msgrcv");
continue;
}
ACCOUNT acc;
CLOSE_RESPOND res;
if(get(req.id,&acc) == -1)//是否存在该账户
{
sprintf(res.msg,"%s无效帐号",req.id);
goto send;
}
if(strcmp(req.name,acc.name))//比较用户名是否正确
{
sprintf(res.msg,"%s用户名错误",req.id);
goto send;
}
if(strcmp(req.pw,acc.pw)) //比较密码是否正确
{
sprintf(res.msg,"%s密码错误",req.id);
goto send;
}
if(delete(req.id) == -1)
{
sprintf(res.msg,"%s删除账户失败",req.id);
goto send;
}
res.money = acc.money;
sprintf(res.msg,"%s销户成功",req.id);
send:
res.mtype = 6;
if(msgsnd(g_resid,&res,sizeof(res)-sizeof(long),0)==-1)
{
perror("perror");
continue;
}
}
return 0;
}
int delete(char id[100]) //删除账户
{
char path[256];
sprintf(path,"%s.txt",id);
if(unlink(path)==-1)
{
perror("unlink");
return -1;
}
return 0;
}
int get(char id[100],ACCOUNT *acc) //读取文件
{
char pathname[256];
sprintf(pathname,"%s.txt",id);
int fd =open(pathname,O_RDONLY);
if(fd == -1)
{
perror("open");
return -1;
}
if(read(fd,acc,sizeof(*acc))==-1)
{
perror("read");
return -1;
}
close(fd);
return 0;
}
客户端进程:
可以先写一个界面,类似C语言的学员管理系统,根据界面的操作提示选择相对应的功能
操作有:
(1)开户
(2)存款
(3)取款
(4)查询
(5)转账
(6)销户
(7)退出
实现:首先要获取消息队列
1)开户操作
开户的时候榕湖需要输入用户的相关信息(账户、密码、存款金额),为了操作简便可以通过结构体来封装用户信息,输入信息操作后将消息发送到消息队列,并指定消息类型。
2)存款:向指定的账户存入指定的金额,先输入用户相关信息,将信息发送到消息队列,并指定消息类型
3)取款:先输入用户相关信息和取款金额,将信息发送到消息队列,并指定消息类型
4)查询:先输入用户相关信息,将信息发送到消息队列,并指定消息类型
5)转账:先输入当前用户相关信息和要转账到的用户信息,将信息发送到消息队列,并指定消息类型
6)销户:先输入销毁用户信息,将信息发送到消息队列,并指定消息类型
客户端代码如下所示:
#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>
int g_reqid; //作为请求的消息队列的返回id
int g_resid; //作为应答消息队列的返回id
typedef struct msgbuf{
long mtype; //消息类型
char id[100]; //账户
char name[256]; //用户名
char pw[9]; //密码
float money; //银行卡余额
}userm;
typedef struct tag_OpenRespond{
long type;
char id[100]; //账户
char msg[256];
float money; //银行卡余额
}RES; //让客户端从应答消息队列读到的反馈存到这里
void meun();
void open_kh(); //开户
void close_kh(); //注销用户
void save(); //存款业务
void withdraw(); //取款业务
void query(); //查询业务
void trans(); //转账业务
int main()
{
while(1)
{
meun();
int a;
printf("请输入要执行的操作!\n");
scanf("%d",&a);
switch(a)
{
case 1:open_kh();break;
case 2:save();break;
case 3:withdraw();break;
case 4:query();break;
case 5:trans();break;
case 6:close_kh();break;
case 7:
return 0;
}
}
}
void open_kh()
{
key_t kid1,kid2;
kid1 = ftok("/home/whs/002/0706",1); //键值
kid2 = ftok("/home/whs/002/0706",2);
g_reqid = msgget(kid1,IPC_CREAT|0644); //打开请求消息队列
if(g_reqid== -1)
{
perror("msgget");
return;
}
g_resid =msgget(kid2,IPC_CREAT|0644); //打开应答消息队列
if(g_resid == -1)
{
perror("msgget");
return;
}
userm a;
printf("请输入要注册的身份号!\n");
scanf("%s",a.id);
printf("请输入要注册的用户名!\n");
scanf("%s",a.name);
printf("请输入要注册的密码!\n");
scanf("%s",a.pw);
printf("请输入要注册的余额!\n");
scanf("%f",&a.money);
//往请求消息队列写
a.mtype = 1; //消息类型为1,开户用
msgsnd(g_reqid,&a,sizeof(userm)-sizeof(long),0);
//读应答消息队列
RES buf;
msgrcv(g_resid,&buf,sizeof(buf),1,0); //1类消息,开户用
printf("%s\n",buf.msg);
return;
}
void close_kh()
{
key_t kid1,kid2;
kid1 = ftok("/home/whs/002/0706",1); //键值
kid2 = ftok("/home/whs/002/0706",2);
g_reqid = msgget(kid1,IPC_CREAT|0644); //打开请求消息队列
if(g_reqid== -1)
{
perror("msgget");
return;
}
g_resid =msgget(kid2,IPC_CREAT|0644); //打开应答消息队列
if(g_resid == -1)
{
perror("msgget");
return;
}
userm b;
printf("请输入要注销用户的身份号!\n");
scanf("%s",b.id);
printf("请输入要注销用户的用户名!\n");
scanf("%s",b.name);
printf("请输入要注销用户的密码!\n");
scanf("%s",b.pw);
printf("请输入要注销用户的余额!\n");
scanf("%f",&b.money);
//往请求消息队列写
b.mtype = 6; //消息类型为6,注销用
msgsnd(g_reqid,&b,sizeof(userm)-sizeof(long),0);
//读应答消息队列
RES buf1;
msgrcv(g_resid,&buf1,sizeof(buf1),6,0); //6类消息,注销用
printf("%s\n",buf1.msg);
return;
}
void save()//存款业务
{
key_t kid1,kid2;
kid1 = ftok("/home/whs/002/0706",1); //键值
kid2 = ftok("/home/whs/002/0706",2);
g_reqid = msgget(kid1,IPC_CREAT|0644); //打开请求消息队列
if(g_reqid== -1)
{
perror("msgget");
return;
}
g_resid =msgget(kid2,IPC_CREAT|0644); //打开应答消息队列
if(g_resid == -1)
{
perror("msgget");
return;
}
userm c;
printf("请输入要存款用户的身份号!\n");
scanf("%s",c.id);
printf("请输入要存款用户的用户名!\n");
scanf("%s",c.name);
printf("请输入要存款用户的密码!\n");
scanf("%s",c.pw);
printf("请输入要存款的金额!\n");
scanf("%f",&c.money);
//往请求消息队列写
c.mtype = 2; //消息类型为2,存款用
msgsnd(g_reqid,&c,sizeof(userm)-sizeof(long),0);
//读应答消息队列
RES buf2;
msgrcv(g_resid,&buf2,sizeof(buf2),2,0); //2类消息,存款用
printf("%s\n",buf2.msg);
return;
}
void withdraw()//取款业务
{
key_t kid1,kid2;
kid1 = ftok("/home/whs/002/0706",1); //键值
kid2 = ftok("/home/whs/002/0706",2);
g_reqid = msgget(kid1,IPC_CREAT|0644); //打开请求消息队列
if(g_reqid== -1)
{
perror("msgget");
return;
}
g_resid =msgget(kid2,IPC_CREAT|0644); //打开应答消息队列
if(g_resid == -1)
{
perror("msgget");
return;
}
userm d;
printf("请输入要取款用户的身份号!\n");
scanf("%s",d.id);
printf("请输入要取款用户的用户名!\n");
scanf("%s",d.name);
printf("请输入要取款用户的密码!\n");
scanf("%s",d.pw);
printf("请输入要取款的金额!\n");
scanf("%f",&d.money);
//往请求消息队列写
d.mtype = 3; //消息类型为3,取款用
msgsnd(g_reqid,&d,sizeof(userm)-sizeof(long),0);
//读应答消息队列
RES buf3;
msgrcv(g_resid,&buf3,sizeof(buf3),3,0); //3类消息,取款用
printf("%s\n",buf3.msg);
return;
}
void query()//查询业务
{
key_t kid1,kid2;
kid1 = ftok("/home/whs/002/0706",1); //键值
kid2 = ftok("/home/whs/002/0706",2);
g_reqid = msgget(kid1,IPC_CREAT|0644); //打开请求消息队列
if(g_reqid== -1)
{
perror("msgget");
return;
}
g_resid =msgget(kid2,IPC_CREAT|0644); //打开应答消息队列
if(g_resid == -1)
{
perror("msgget");
return;
}
userm e;
printf("请输入要查询用户的身份号!\n");
scanf("%s",e.id);
printf("请输入要查询用户的用户名!\n");
scanf("%s",e.name);
printf("请输入要查询用户的密码!\n");
scanf("%s",e.pw);
//printf("请输入要查询的金额!\n");
//scanf("%f",&d.money);
//往请求消息队列写
e.mtype = 4; //消息类型为4,查询用
msgsnd(g_reqid,&e,sizeof(userm)-sizeof(long),0);
//读应答消息队列
RES buf4;
msgrcv(g_resid,&buf4,sizeof(buf4),4,0); //消息类型为4,查询用
printf("%s\n",buf4.msg);
return;
}
void trans()//转账业务
{
key_t kid1,kid2;
kid1 = ftok("/home/whs/002/0706",1); //键值
kid2 = ftok("/home/whs/002/0706",2);
g_reqid = msgget(kid1,IPC_CREAT|0644); //打开请求消息队列
if(g_reqid== -1)
{
perror("msgget");
return;
}
g_resid =msgget(kid2,IPC_CREAT|0644); //打开应答消息队列
if(g_resid == -1)
{
perror("msgget");
return;
}
userm f1,f2;
printf("请输入转账用户的身份号!\n");
scanf("%s",f1.id);
printf("请输入转账用户的用户名!\n");
scanf("%s",f1.name);
printf("请输入转账用户的密码!\n");
scanf("%s",f1.pw);
printf("请输入要转账的金额!\n");
scanf("%f",&f1.money);
printf("请输入被转账用户的身份号!\n");
scanf("%s",f2.id);
//往请求消息队列写
f1.mtype = 5; //消息类型为5,转账用,第一条消息,转账用户用
msgsnd(g_reqid,&f1,sizeof(userm)-sizeof(long),0);
f2.mtype = 55; //消息类型为55,转账用,第二条消息,被转账用户用
msgsnd(g_reqid,&f2,sizeof(userm)-sizeof(long),0);
//读应答消息队列
RES buf3;
msgrcv(g_resid,&buf3,sizeof(buf3),5,0); //5类消息,转账用
printf("%s\n",buf3.msg);
RES buf4;
msgrcv(g_resid,&buf4,sizeof(buf4),55,0); //55类消息,转账用
printf("%s\n",buf4.msg);
return;
}
void meun()
{
printf("***********欢迎来到牛逼银行!************\n");
printf("**************1.开户******************\n");
printf("**************2.存款******************\n");
printf("**************3.取款******************\n");
printf("**************4.查询******************\n");
printf("**************5.转账******************\n");
printf("**************6.销户******************\n");
printf("**************7.退出******************\n");
}
四、运行测试
项目通过文件操作完成,所有.c和可执行文件以及保存的用户的文件保存在一个文件夹下(注意:保存的用户信息的文件在终端直接打开后是乱码,但是在运行中可以正常读取)
打开两个终端,一个运行客户端,一个运行服务端
首先是开户:
存款操作:
取款操作:
查询操作:
转账操作:
注销用户:
客户端退出,服务端杀死所有进程并给出提示:
更多推荐
所有评论(0)