一、实现的功能:(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和可执行文件以及保存的用户的文件保存在一个文件夹下(注意:保存的用户信息的文件在终端直接打开后是乱码,但是在运行中可以正常读取)

 打开两个终端,一个运行客户端,一个运行服务端

首先是开户:

 

存款操作:

 

取款操作:

 

查询操作:

 

转账操作:

 

注销用户:

 

客户端退出,服务端杀死所有进程并给出提示:

 

 

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐