ATM机 练习

//客户端和服务器共用的头文件
#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;
}



































  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值