用消息队列实现银行管理系统项目。

银行管理系统项目:

  主要有六个功能:
        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函数去销户等等,是对系统编程的进程通信一个综合实践的小项目。

        希望可以给大家带来思路,欢迎大家点赞评论和分享哦。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值