银行管理系统项目:
主要有六个功能:
1开户
3存钱
4取钱
5转账
6查询
服务器端:
获取两个消息队列的id
开进程调用六个功能执行execl 函数族关闭服务器的时候会删除消息队列杀死六个子进程
客户端:
输入信息发送到消息队列接受消息队列的信息并做处理.、
2销户
1.先写一个共用的.h文件,里面包括常用的头文件和公用的消息队列传输的结构体,主要包括用户名,用户id,用户密码,用户金额。
#ifndef _COMMON_H_
#define _COMMON_H_
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#define type_open 1
#define type_memory 2
#define type_take 3
#define type_query 4
#define type_transfer 5
#define type_delete 6
#define SER_ID 0x123456 //服务器消息队列通道
#define CLI_ID 0x654321 //客户端消息队列通道
struct msg{
long type;
int id;
char passward[20];
char name[20];
char error[30];
float money;
};
#endif
2. 写一个服务器开户程序
open.c消息队列接收信息对信息做处理保存账号(创一个文件将对应的信息保存在里面)给客户端回消息
客户端开户
输入账号信息给服务器发送
#include "common.h"
/*开户功能
1.从消息队列接受信息
2.对该用户分配账号
3.对该用户进行信息保存
4.返回信息
*/
struct msg ser = {0};
int assigning(void);
int sava(void);
int main()
{
int id;
int ser_id = msgget(SER_ID,IPC_CREAT|0666);
if(ser_id == -1)
{
perror("msgget");
return 0;
}
int cli_id = msgget(CLI_ID,IPC_CREAT|0666);
if(cli_id == -1)
{
perror("msgget");
return 0;
}
printf("开户功能运行。。。\n");
while(1)
{
memset(&ser,0,sizeof(ser));
msgrcv(ser_id,&ser,sizeof(ser)-8,type_open,0);
ser.type = type_open;
if(ser.money<0.0)
{
sprintf(ser.error,"开户的钱不能少于0");
printf("开户的钱不能少于0\n");
}
else
{
id = assigning();
//printf("id = %d\n",id);
//保存用户开户信息
ser.id = id;
sava();
}
msgsnd(cli_id,&ser,sizeof(ser)-8,0);
}
return 0;
}
int assigning(void)
{
int fd = open("./data/user.txt",O_RDWR|O_CREAT,0666);
if(fd == -1)
{
perror("open");
return -1;
}
int id;
int ret = read(fd,&id,4);//去读文件里有没有开户id,没有的话说明是第一次开户,我们手动赋值;(返回值为读到的字节数)
if(ret == 0)
{
id = 20230000;//手动分配id
}
else
{
id++;//说明里面存有id 且已经被用了
lseek(fd,0,SEEK_SET);
}
write(fd,&id,4);
close(fd);
return id;
}
int sava(void)
{
char buff[100];
sprintf(buff,"./data/%d.txt",ser.id);
int fd = open(buff,O_RDWR|O_CREAT,0666);
if(fd == -1)
{
perror("open");
return -1;
}
write(fd,&ser,sizeof(ser));
printf("保存成功\n");
close(fd);
return 0;
}
3.销户功能,先用access()函数去判断文件是不是存在,然后用remove()函数去删除文件。
#include "common.h"
/*
确定需要注销的账户(以id进行删除);
删除相应的文件;
返还对应的数字金额
*/
struct msg ser;
struct msg cli;
char buff[40];
int main()
{
int ser_id = msgget(SER_ID,IPC_CREAT|0666);
if(ser_id == -1)
{
perror("msgget");
return 0;
}
int cli_id = msgget(CLI_ID,IPC_CREAT|0666);
if(cli_id == -1)
{
perror("msgget");
return 0;
}
printf("销户功能运行中。。。\n");
while(1)
{
memset(&ser,0,sizeof(ser));
msgrcv(ser_id,&cli,sizeof(ser)-8,type_delete,0);
sprintf(buff,"./data/%d.txt",cli.id);
if(access(buff,F_OK) == -1)
{
sprintf(cli.error,"用户不存在");
}
else
{
int fd = open(buff,O_RDWR);
if(fd==-1)
{
perror("open");
return 0;
}
read(fd,&ser,sizeof(ser));
close(fd);
if(strcmp(ser.passward,cli.passward))
{
sprintf(cli.error,"密码不正确");
}
else
{
if(remove(buff)==0)
printf("删除成功\n");
}
}
cli.money = ser.money;
msgsnd(cli_id,&cli,sizeof(ser)-8,0);
}
return 0;
}
4.存款功能。在客户端发送存款请求,同时发送用户id,取款金额,在服务器端用strcmp()函数进行判定id和密码是否正确。
#include "common.h"
struct msg ser;
struct msg cli;
char buff[40];
int main()
{
int ser_id = msgget(SER_ID,IPC_CREAT|0666);
if(ser_id == -1)
{
perror("msgget");
return 0;
}
int cli_id = msgget(CLI_ID,IPC_CREAT|0666);
if(cli_id == -1)
{
perror("msgget");
return 0;
}
printf("存钱功能运行中。。。\n");
while(1)
{
memset(&ser,0,sizeof(ser));
msgrcv(ser_id,&cli,sizeof(ser)-8,type_memory,0);
sprintf(buff,"./data/%d.txt",cli.id);
//ser.type = type_memory;
if(access(buff,F_OK) == -1)
{
sprintf(cli.error,"用户不存在");
printf("用户不存在\n");
}
else
{
int fd = open(buff,O_RDWR);
if(fd==-1)
{
perror("open");
return 0;
}
read(fd,&ser,sizeof(ser));
if(strcmp(ser.passward,cli.passward))
{
sprintf(cli.error,"密码不正确");
printf("密码不正确\n");
}
else
{
ser.money=ser.money+cli.money;
printf("存款成功\n");
}
lseek(fd,0,SEEK_SET);
write(fd,&ser,sizeof(ser));
close(fd);
}
strcpy(cli.name,ser.name);
cli.money = ser.money;
msgsnd(cli_id,&cli,sizeof(ser)-8,0);
}
}
5.取款功能,代码和存款功能相似。
#include "common.h"
struct msg ser;
struct msg cli;
char buff[40];
int main()
{
int ser_id = msgget(SER_ID,IPC_CREAT|0666);
if(ser_id == -1)
{
perror("msgget");
return 0;
}
int cli_id = msgget(CLI_ID,IPC_CREAT|0666);
if(cli_id == -1)
{
perror("msgget");
return 0;
}
printf("查询功能运行中。。。\n");
while(1)
{
memset(&ser,0,sizeof(ser));
msgrcv(ser_id,&cli,sizeof(ser)-8,type_query,0);
sprintf(buff,"./data/%d.txt",cli.id);
//ser.type = type_query;
if(access(buff,F_OK) == -1)
{
sprintf(cli.error,"用户不存在");
printf("用户不存在\n");
}
else
{
int fd = open(buff,O_RDWR);
if(fd==-1)
{
perror("open");
return 0;
}
read(fd,&ser,sizeof(ser));
if(strcmp(ser.passward,cli.passward))
{
sprintf(cli.error,"密码错误");
printf("密码错误\n");
}
else
{
cli = ser;
cli.type = type_query;
printf("查询成功\n");
}
close(fd);
}
msgsnd(cli_id,&cli,sizeof(ser)-8,0);
}
}
6.查询功能。代码和存款取款功能相似,只是没有金额变动。
#include "common.h"
struct msg ser;
struct msg cli;
char buff[40];
int main()
{
int ser_id = msgget(SER_ID,IPC_CREAT|0666);
if(ser_id == -1)
{
perror("msgget");
return 0;
}
int cli_id = msgget(CLI_ID,IPC_CREAT|0666);
if(cli_id == -1)
{
perror("msgget");
return 0;
}
printf("查询功能运行中。。。\n");
while(1)
{
memset(&ser,0,sizeof(ser));
msgrcv(ser_id,&cli,sizeof(ser)-8,type_query,0);
sprintf(buff,"./data/%d.txt",cli.id);
//ser.type = type_query;
if(access(buff,F_OK) == -1)
{
sprintf(cli.error,"用户不存在");
printf("用户不存在\n");
}
else
{
int fd = open(buff,O_RDWR);
if(fd==-1)
{
perror("open");
return 0;
}
read(fd,&ser,sizeof(ser));
if(strcmp(ser.passward,cli.passward))
{
sprintf(cli.error,"密码错误");
printf("密码错误\n");
}
else
{
cli = ser;
cli.type = type_query;
printf("查询成功\n");
}
close(fd);
}
msgsnd(cli_id,&cli,sizeof(ser)-8,0);
}
}
7.转账功能。这个应该是所有功能里最难的一个了,我用的是往客户端发送两个结构体,第一个是转账发起人的用户信息,第二个是转账接收人的用户id,经过服务器比对确认双方信息无误完成转账。转账成功后服务器发送回双方的账户信息。
#include "common.h"
struct msg ser;
struct msg ser1;
struct msg cli;
struct msg cli1;
char buff[40];
char buff1[40];
int main()
{
int ser_id = msgget(SER_ID,IPC_CREAT|0666);
if(ser_id == -1)
{
perror("msgget");
return 0;
}
int cli_id = msgget(CLI_ID,IPC_CREAT|0666);
if(cli_id == -1)
{
perror("msgget");
return 0;
}
printf("转账功能运行中。。。\n");
while(1)
{
memset(&ser,0,sizeof(ser));
msgrcv(ser_id,&cli,sizeof(ser)-8,type_transfer,0);
msgrcv(ser_id,&cli1,sizeof(ser)-8,type_transfer,0);
sprintf(buff,"./data/%d.txt",cli.id);
sprintf(buff1,"./data/%d.txt",cli1.id);
//ser.type = type_transfer;
if(access(buff,F_OK) == -1 || access(buff1,F_OK) == -1)
{
if(access(buff,F_OK) == -1)
{sprintf(cli.error,"转账发起账户不存在");
printf("转账发起账户不存在\n");}
else
{sprintf(cli.error,"转账接受用户不存在");
printf("转账接受用户不存在\n");}
}
else
{
int fd = open(buff,O_RDWR);
int fd1 = open(buff1,O_RDWR);
read(fd,&ser,sizeof(ser));
read(fd1,&ser1,sizeof(ser1));
if(strcmp(ser.passward,cli.passward))
{
sprintf(cli.error,"用户密码不正确");
printf("用户密码不正确\n");
}
else
{
ser.money=ser.money-cli.money;
ser1.money=ser1.money+cli.money;
if(ser.money < 0)
{
sprintf(cli.error,"你过分了,贪得无厌\n");
ser.money=ser.money+cli.money;
ser1.money=ser1.money-cli.money;
printf("你过分了,贪得无厌\n");
}
else
printf("转账成功\n");
}
lseek(fd,0,SEEK_SET);
lseek(fd1,0,SEEK_SET);
write(fd,&ser,sizeof(ser));
write(fd1,&ser1,sizeof(ser1));
close(fd);
close(fd1);
cli.money = ser.money;
cli1.money = ser1.money;
strcpy(cli.name,ser.name);
strcpy(cli1.name,ser1.name);
}
msgsnd(cli_id,&cli,sizeof(ser)-8,0);
msgsnd(cli_id,&cli1,sizeof(ser)-8,0);
}
}
8.服务器端:用一个指针数组把6个功能的地址存进,在分别fork出6个子进程用execl拉起6个功能.在中止服务器后,杀死6个子进程并删除消息队列。
#include "common.h"
/*
1 开启六个子进程
2 服务器退出的时候 关闭六个子进程
3 退出时删除消息队列
*/
pid_t pid[10];
char * func[]={"./open","./delete","./memory","./take","./query","./transfer"};
#define Len (sizeof(func)/sizeof(char*))
int main()
{
int ser_id = msgget(SER_ID,IPC_CREAT|0644);
if(ser_id == -1)
{
printf("ser创建消息队列失败\n");
return -1;
}
int cli_id = msgget(CLI_ID,IPC_CREAT|0644);
if(cli_id == -1)
{
printf("cli创建消息队列失败\n");
return -1;
}
//创建子进程
for(int i = 0;i<Len;i++)
{
pid[i] = vfork();
if(pid[i] == 0)
{
execl(func[i],func[i],NULL);
}
}
printf("服务器正在运行 按下回车结束\n");
getchar();
//杀死子进程
for(int i = 0;i<Len;i++)
{
kill(pid[i],2);
}
msgctl(cli_id,IPC_RMID,NULL);
msgctl(ser_id,IPC_RMID,NULL);
printf("服务器结束运行\n");
return 0;
}
9.客户端里包含用户菜单,以及各项功能请求。
#include "common.h"
/*
客户端
*/
struct msg cli;
int ser_id;
int cli_id;
int menu(void);
void openacc(void);
void take(void);
void memory(void);
void query(void);
void transfer(void);
void delete(void);
int main()
{
ser_id = msgget(SER_ID,IPC_CREAT|0666);
if(ser_id == -1)
{
perror("msgget");
return 0;
}
cli_id = msgget(CLI_ID,IPC_CREAT|0666);
if(cli_id == -1)
{
perror("msgget");
return 0;
}
int choice;
while(1)
{
choice = menu();
switch(choice)
{
case 1: openacc(); break;
case 2: delete();break;
case 3: memory(); break;
case 4: take();break;
case 5: transfer();break;
case 6: query();break;
case 0: return 0;
default:printf("输入不对,请输入0-6。\n");break;
}
}
return 0;
}
int menu(void)
{
printf("-----1 开户-----\n");
printf("-----2 销户-----\n");
printf("-----3 存钱-----\n");
printf("-----4 取钱-----\n");
printf("-----5 转账-----\n");
printf("-----6 查询-----\n");
printf("-----0 退出-----\n");
int a;
scanf("%d",&a);
return a;
}
void openacc(void)
{
memset(&cli,0,sizeof(cli));
printf("开户ing\n");
printf("请输入用户名:");
scanf("%s",cli.name);
printf("请输入密码:");
scanf("%s",cli.passward);
printf("请输入开户的金额(>0):");
scanf("%f",&cli.money);
cli.type = type_open;
msgsnd(ser_id,&cli,sizeof(cli)-8,0);
msgrcv(cli_id,&cli,sizeof(cli)-8,type_open,0);
if(strlen(cli.error ) == 0)
{
printf("开户成功\n");
printf("卡号为:%d\n",cli.id);
printf("用户名为:%s\n",cli.name);
printf("密码为:%s\n",cli.passward);
printf("金额为:%.2f\n",cli.money);
}
else
{
printf("****************\n");
printf("%s\n",cli.error);
printf("****************\n");
}
}
void memory(void)
{
memset(&cli,0,sizeof(cli));
printf("存钱ing。。。\n");
printf("请输入用户id:");
scanf("%d",&cli.id);
printf("请输入密码:");
scanf("%s",cli.passward);
printf("请输入存储的金额(>0):");
scanf("%f",&cli.money);
cli.type = type_memory;
msgsnd(ser_id,&cli,sizeof(cli)-8,0);
msgrcv(cli_id,&cli,sizeof(cli)-8,type_memory,0);
if(strlen(cli.error ) == 0)
{
printf("*****储存成功*******\n");
printf("卡号为: %d\n",cli.id);
printf("用户名为: %s\n",cli.name);
printf("金额为: %.2f\n",cli.money);
}
else
{
printf("****************\n");
printf("%s\n",cli.error);
printf("****************\n");
}
}
void take(void)
{
memset(&cli,0,sizeof(cli));
printf("取钱ing。。。\n");
printf("请输入用户id:");
scanf("%d",&cli.id);
printf("请输入密码:");
scanf("%s",cli.passward);
printf("请输入取款的金额(>0):");
scanf("%f",&cli.money);
cli.type = type_take;
msgsnd(ser_id,&cli,sizeof(cli)-8,0);
msgrcv(cli_id,&cli,sizeof(cli)-8,type_take,0);
if(strlen(cli.error ) == 0)
{
printf("*****取款成功*******\n");
printf("卡号为: %d\n",cli.id);
printf("用户名为: %s\n",cli.name);
printf("金额为: %.2f\n",cli.money);
}
else
{
printf("****************\n");
printf("%s\n",cli.error);
printf("****************\n");
}
}
void query(void)
{
memset(&cli,0,sizeof(cli));
printf("查询ing。。。\n");
printf("请输入用户id:");
scanf("%d",&cli.id);
printf("请输入密码:");
scanf("%s",cli.passward);
cli.type = type_take;
msgsnd(ser_id,&cli,sizeof(cli)-8,0);
msgrcv(cli_id,&cli,sizeof(cli)-8,type_take,0);
if(strlen(cli.error) == 0)
{
printf("*****查询成功*******\n");
printf("卡号为: %d\n",cli.id);
printf("用户名为: %s\n",cli.name);
printf("金额为: %.2f\n",cli.money);
}
else
{
printf("****************\n");
printf("%s\n",cli.error);
printf("****************\n");
}
}
void transfer(void)
{
memset(&cli,0,sizeof(cli));
struct msg cli1={0};
printf("转账ing。。。\n");
printf("请输入用户id:");
scanf("%d",&cli.id);
printf("请输入密码:");
scanf("%s",cli.passward);
printf("请输入转入账户id:");
scanf("%d",&cli1.id);
printf("请输入转入账户金额:");
scanf("%f",&cli.money);
cli.type = type_transfer;
cli1.type = type_transfer;
msgsnd(ser_id,&cli,sizeof(cli)-8,0);
msgsnd(ser_id,&cli1,sizeof(cli)-8,0);
msgrcv(cli_id,&cli,sizeof(cli)-8,type_transfer,0);
msgrcv(cli_id,&cli1,sizeof(cli)-8,type_transfer,0);
if(strlen(cli.error) == 0)
{
printf("*****转账成功*******\n");
printf("卡号为: %d\n",cli.id);
printf("用户名为: %s\n",cli.name);
printf("金额为: %.2f\n",cli.money);
printf("****************\n");
printf("卡号为: %d\n",cli1.id);
printf("用户名为: %s\n",cli1.name);
printf("金额为: %.2f\n",cli1.money);
}
else
{
printf("****************\n");
printf("%s\n",cli.error);
printf("****************\n");
}
}
void delete(void)
{
memset(&cli,0,sizeof(cli));
printf("删除ing。。。\n");
printf("请输入用户id:");
scanf("%d",&cli.id);
printf("请输入密码:");
scanf("%s",cli.passward);
cli.type = type_delete;
msgsnd(ser_id,&cli,sizeof(cli)-8,0);
msgrcv(cli_id,&cli,sizeof(cli)-8,type_delete,0);
if(strlen(cli.error) == 0)
{
printf("*****删除成功*******\n");
printf("卡号为: %d\n",cli.id);
printf("用户名为: %s\n",cli.name);
printf("剩余金额为: %.2f\n",cli.money);
}
else
{
printf("****************\n");
printf("%s\n",cli.error);
printf("****************\n");
}
}
这个项目的代码量大概在一千行左右,大部分功能的代码相似。主要用消息队列进行进程间通信,用到了文件的打开,保存,读取,关闭用来保存银行用户信息,保证即使重开服务器或者断开程序仍能保存用户的各项信息。同时还用了access()函数用来验证文件是否存在,用remove函数去销户等等,是对系统编程的进程通信一个综合实践的小项目。
希望可以给大家带来思路,欢迎大家点赞评论和分享哦。