#ifndef __BANK_H__
#define __BANK_H__
//两个消息队列的身份证号
#define C2S_KEY 10001
#define S2C_KEY 10002
//消息的类型
#define M_OPEN 1//开户类型消息
#define M_DESTROY 2//销户
#define M_SAVE 3//存款
#define M_TAKE 4//取款
#define M_QUERY 5//查询
#define M_TRANS 6//转帐
#define M_SUCCESS 7//处理成功
#define M_FAILED 8//处理失败
//包含账户信息的结构体
typedef struct
{
int id;//账号
char name[10];//用户名
char password[10];//密码
double balance;//账户余额
} account;
//消息队列中有关消息类型的结构体
typedef struct
{
long mtype;//消息类型
account acc;//账户信息结构体
} msg;
#endif
服务器端
# include "./bank.h"
# include <stdio.h>
# include <unistd.h>
# include <signal.h>
#include <stdlib.h>
# include <sys/msg.h>
# include <sys/ipc.h>
# include <fcntl.h>
# include <sys/wait.h>
static int c2s_msgid = 0;
static int s2c_msgid = 0;
pid_t process_id = 0;
//ctrl+c退出,杀死子进程,销毁消息队列
void sig_exit(int signum)
{
printf("捕捉到信号SIGINT,开始kill子进程和销毁信息队列\n");
//杀死子进程
kill(process_id, SIGINT);
//销毁信息队列
msgctl(c2s_msgid, IPC_RMID, NULL);
msgctl(s2c_msgid, IPC_RMID, NULL);
printf("服务器关闭成功!\n");
//退出
exit(0);
}
/*产生一个不重复的用户id*/
int generator_id(void)
{
int fd = 0;
int acc_id = 1000001;
/*判断文件是否存在*/
if(-1 == access(".id.dat", F_OK))
{
//不存在则证明是第一次运行server
fd = open(".id.dat", O_WRONLY|O_CREAT, 0600);
}
else
{
//文件存在,则证明不是第一次运行server
fd = open(".id.dat", O_RDWR);
//从文件中读出要分配给账户的id
read(fd, &acc_id, sizeof(int));
//文件写入位置回到文件头,以便后续重新打开读取到新的id
lseek(fd, 0, SEEK_SET);
}
//将acc_id更新为下次要使用的值,并将它写入到文件id.dat中
acc_id++;
write(fd, &acc_id, sizeof(int));
//将本次分配的用户id返回
close(fd);
return (acc_id - 1);
}
//保存用户信息到以用户id命名的.dat文件中
int store_acc(account *acc)
{
char filename[20] = {0};
//使文件名为用户名称
sprintf(filename, "%d.dat", acc->id);
//创建用户个人信息文件
int fd = open(filename, O_RDWR|O_CREAT, 0600);
if(fd < 0)
{
perror("open failed");
return -1;
}
write(fd, acc, sizeof(account));
close(fd);
return 0;
}
//创建用户信息
int creat_acc(msg *client_msg)
{
int ret = 0;
/*补充acc->id 文件操作*/
client_msg->acc.id = generator_id();
/*把acc信息写入id.dat*/
ret = store_acc(&(client_msg->acc));
/*向s2c_msgid对应的队列中写入操作结果信息*/
client_msg->mtype = ret? M_FAILED:M_SUCCESS;
if(-1 == msgsnd(s2c_msgid, client_msg, sizeof(client_msg->acc), 0))
{
perror("msgsnd failed");
exit(-1);
}
//printf("客户端创建开户成功\n");
return 0;
}
//服务器根据用户ID查询用户信息
void query_user(int id)
{
msg msg_user = {0};
msg_user.mtype = M_QUERY;
char buf[20] = {0};
sprintf(buf, "%d.dat", id);
//printf("用户名拼接成功为:%s\n", buf);
//服务器得到用户id,打开之匹配的文件
int fd = open(buf, O_RDONLY);
if(-1 == fd)
{
perror("open failed");
exit(-1);
}
//printf("打开文件成功!\n");
//读取打开的文件
if(-1 == read(fd, &msg_user.acc, sizeof(msg_user.acc)))
{
perror("read failed");
exit(-1);
}
//printf("读取文件成功!\n");
//传递给客户端
if(-1 == msgsnd(s2c_msgid, &msg_user, sizeof(msg_user.acc), 0))
{
perror("msgsnd failed");
exit(-1);
}
printf("客户端查询成功!\n");
return;
}
//服务器销户
void delete_acc(int id)
{
query_user(id);
char buf[20] = {0};
sprintf(buf, "%d.dat", id);
//创建子进程
pid_t delete_pid = vfork();
if(0 == delete_pid)
{
execlp("rm", "rm", buf, NULL);
}
else if(delete_pid > 0)
{
int status = 0;
pid_t res = wait(&status);
if(WIFEXITED(status))
{
printf("客户端销户成功!\n");
return;
}
}
}
//服务器存钱
void save_acc(msg msg_save)
{
//查找文件
char buf[20] = {0};
sprintf(buf, "%d.dat", msg_save.acc.id);
//打开该文件
int fd = open(buf, O_RDWR);
if(-1 == fd)
{
perror("open failed");
exit(-1);
}
//printf("打开文件成功\n");
//改写文件内存款金额
lseek(fd, sizeof(msg_save.acc)-sizeof(double), SEEK_SET);
if(-1 == write(fd, &msg_save.acc.balance, sizeof(double)))
{
perror("write failed");
exit(-1);
}
//printf("写入文件成功\n");
lseek(fd, 0, SEEK_SET);
//读取文件内容
if(-1 == read(fd, &msg_save.acc, sizeof(msg_save.acc)))
{
perror("read failed");
exit(-1);
}
//printf("读取文件成功\n");
close(fd);
//向s2c_msgid消息队列发送消息
if(-1 == msgsnd(s2c_msgid, &msg_save, sizeof(msg_save.acc), 0))
{
perror("msgsnd failed");
exit(-1);
}
if(msg_save.mtype == M_SAVE)
{
printf("客户端存款成功!\n");
}
else if(msg_save.mtype == M_TAKE)
{
printf("客户端取款成功!\n");
}
return;
}
//取钱
void take_acc(msg msg_take)
{
save_acc(msg_take);
return;
}
int process_request(void)
{
int ret = 0;
//SIGINT信号在子进程中采用默认的处理方式
signal(SIGINT, SIG_DFL);
/*msgrcv不阻塞的读c2s_msgid的消息队列*/
while(1)
{
//printf("等待接收客户端信息\n");
msg client_msg = {0};
ret = msgrcv(c2s_msgid, &client_msg, sizeof(client_msg.acc), -6, 0);
if(-1 == ret)
{
perror("msgrcv failed");
exit(-1);
}
//printf("接收客户端信息成功!\n");
/*根据消息类型完成对应的数据处理*/
switch(client_msg.mtype)
{
case M_OPEN:
creat_acc(&client_msg);
break;
case M_DESTROY:
delete_acc(client_msg.acc.id);
break;
case M_SAVE:
save_acc(client_msg);
break;
case M_TAKE:
take_acc(client_msg);
break;
case M_QUERY:
query_user(client_msg.acc.id);
break;
case M_TRANS:
break;
default:
/*消息类型非法,发送消息给用户端*/
printf("非法消息类型...\n");
break;
}
}
}
int main(void)
{
printf("退出服务器请使用ctl+c\n");
/*自定义SIGINT信号的处理方式*/
signal(SIGINT, sig_exit);
signal(SIGQUIT,SIG_IGN);
/*创建两个消息队列, 不存在则创建,存在则获取*/
c2s_msgid = msgget(C2S_KEY, IPC_CREAT|0644);
s2c_msgid = msgget(S2C_KEY, IPC_CREAT|0644);
if(-1 == c2s_msgid||-1 == s2c_msgid)
{
perror("消息队列创建失败!");
exit(-1);//return -1;
}
/*创建子进程*/
pid_t process_id = fork();
if(0 == process_id)
{
process_request();
exit(0);
}
printf("服务器启动成功,等待客户端的请求并处理...\n");
pause();//无限期的睡眠暂停,直到收到信号被中断
printf("服务器进程退出...\n");
return 0;
}
客户端
# include <stdio.h>
# include <stdlib.h>
# include <unistd.h>
# include "./bank.h"
# include <string.h>
static c2s_msgid = 0;
static s2c_msgid = 0;
//获取消息队列
void get_msgqueue(void)
{
c2s_msgid = msgget(C2S_KEY, 0);
s2c_msgid = msgget(S2C_KEY, 0);
if(-1 == c2s_msgid || -1 == s2c_msgid)
{
perror("msgget failed");
exit(-1);
}
//printf("获取消息队列成功!\n");
}
msg get_acc(int acc_id, long mtype)
{
//定义存放要查询的用户数据的变量,并初始化
msg msg_get = {0};
msg_get.acc.id = acc_id;
msg_get.mtype = mtype;
//printf("变量初始化成功\n");
//获取消息队列
get_msgqueue();
//客户端把信息传递给服务器,让服务器进行查询
int ret = msgsnd(c2s_msgid, &msg_get, sizeof(msg_get.acc), 0);
if(-1 == ret)
{
perror("msgsnd failed");
exit(-1);
}
//printf("发送信息成功!\n");
//客户端接收服务器传递的信息
ret = msgrcv(s2c_msgid, &msg_get, sizeof(msg_get.acc), 0, 0);
if(-1 == ret)
{
perror("msgrcv failed");
exit(-1);
}
return msg_get;
}
void creat_user(void)
{
msg *user = (msg*)malloc(sizeof(msg));
/*要求用户输入用户名 密码 金额*/
printf("请输入您的用户名:");
gets(user->acc.name);
printf("请输入您的密码:");
gets(user->acc.password);
user->acc.balance = 10.00;
//消息类型为开户
user->mtype = M_OPEN;
//获取消息队列
get_msgqueue();
/*将消息送入c2s_msgid对应的消息队列*/
if(-1 == msgsnd(c2s_msgid, user, sizeof(user->acc), 0))
{
perror("msgsnd failed");
exit(-1);
}
/*从消息队列s2c_msgid取操作结果并显示*/
msgrcv(s2c_msgid, user, sizeof(user->acc), 0, 0);
if(user->mtype == M_SUCCESS)
{
printf("创建账户成功!\n");
printf("\t账号\t\t用户名\t密码\t金额\n");
printf("\t%d\t\t%s\t%s\t%lg\n",
user->acc.id,
user->acc.name,
user->acc.password,
user->acc.balance);
}
else
{
printf("创建账户失败\n");
}
free(user);
user = NULL;
}
//根据用户ID查询用户信息, 并打印出来
void query_acc(int acc_id)
{
msg msg_query = get_acc(acc_id, M_QUERY);
printf("查询账户成功!\n");
printf("\t账号\t\t用户名\t密码\t金额\n");
printf("\t%d\t\t%s\t%s\t%lg\n",
msg_query.acc.id,
msg_query.acc.name,
msg_query.acc.password,
msg_query.acc.balance);
return;
}
//销户
void delete_user(int delete_id)
{
msg msg_delete = get_acc(delete_id, M_DESTROY);
printf("销户成功:\n");
printf("\t账号\t\t用户名\t密码\t金额\n");
printf("\t%d\t\t%s\t%s\t%lg\n",
msg_delete.acc.id,
msg_delete.acc.name,
msg_delete.acc.password,
msg_delete.acc.balance);
return;
}
//存钱
void save_user(int save_id, double save_money, int flag)
{
msg msg_save = get_acc(save_id, M_QUERY);
if(save_money >= 0)
{
msg_save.mtype = M_SAVE;
}
else
{
msg_save.mtype = M_TAKE;
}
msg_save.acc.balance += save_money;
//获取消息队列
get_msgqueue();
//c2s_msgid 消息队列发送消息
if(-1 == msgsnd(c2s_msgid, &msg_save, sizeof(msg_save.acc), 0))
{
perror("msgsnd failed");
exit(-1);
}
//printf("发送消息成功\n");
//接收s2c_msgid 消息队列的消息
if(-1 == msgrcv(s2c_msgid, &msg_save, sizeof(msg_save.acc), 0, 0))
{
perror("msgrcv failed");
exit(-1);
}
if(1 == flag)
{
if(msg_save.mtype == M_SAVE)
{
printf("存钱成功!\n");
}
else if(msg_save.mtype == M_TAKE)
{
printf("取钱成功!\n");
}
}
printf("\t账号\t\t用户名\t密码\t金额\n");
printf("\t%d\t\t%s\t%s\t%lg\n",
msg_save.acc.id,
msg_save.acc.name,
msg_save.acc.password,
msg_save.acc.balance);
return;
}
//取钱
void take_user(int take_id, double take_money)
{
take_money = 0 - take_money;
save_user(take_id, take_money, 1);
return;
}
//转账
void trans_user(int acc_id, int trans_id, double trans_money)
{
save_user(trans_id, trans_money, 0);
trans_money = 0 - trans_money;
save_user(acc_id, trans_money, 0);
printf("转账成功!\n");
return;
}
/*客户端界面*/
void mainpage(void)
{
int acc_id = 0, trans_id = 0;
double save_money = 0, take_money = 0, trans_money = 0;
while(1)
{
printf("\t欢迎使用迷你ATM机!\n");
printf("------------------------------------\n");
printf("[1]开户\t[2]销户\t[3]存款\t[4]取款\n");
printf("[5]查询\t[6]转帐\t[0]退出\n");
printf("------------------------------------\n");
printf("请选择:");
int user_choose = 0;
scanf("%d", &user_choose);
scanf("%*[^\n]");
scanf("%*c");
switch(user_choose)
{
case M_OPEN:
creat_user();
break;
case M_DESTROY:
printf("请输入您要销户的ID:");
scanf("%d", &acc_id);
//printf("输入成功!\n");
delete_user(acc_id);
break;
case M_SAVE:
printf("请输入您要存款ID:");
scanf("%d", &acc_id);
printf("请输入您要存的金额:");
scanf("%lg", &save_money);
//printf("输入成功!\n");
save_user(acc_id, save_money, 1);
break;
case M_TAKE:
printf("请输入您要取款的ID:");
scanf("%d", &acc_id);
printf("请输入您要取的金额:");
scanf("%lg", &take_money);
//printf("输入成功!\n");
take_user(acc_id, take_money);
break;
case M_QUERY:
printf("请输入您要查询的ID:");
scanf("%d", &acc_id);
//printf("输入成功!\n");
query_acc(acc_id);
break;
case M_TRANS:
printf("请输入您的ID:");
scanf("%d", &acc_id);
printf("请输入对方的ID:");
scanf("%d", &trans_id);
printf("请输入你要转账的金额:");
scanf("%lg", &trans_money);
trans_user(acc_id, trans_id, trans_money);
break;
case 0:
printf("客户端退出成功!\n");
exit(0);
break;
default:
printf("输入错误!\n");
break;
}
}
}
int main(void)
{
mainpage();
return 0;
}