功能描述:1:开户;2:销户;3:存钱;4:取钱;5:查询;6:转账;
主要用的技术:
一:消息队列:
1)key_t key = ftok(".",100); //获取key
2)msgid = msgget(key,IPC_CREATE|0666); //创建 msgid = msgget(key,0); //获取
3)msgsnd = msgsnd = (msgid,&msg,sizeof(msg),0); //发送
4)msgrcv = msgrcv = (msgid,&msg,sizeof(msg),0,0); //获取
5)msgt = msgctl(msgid,IPC_RMID,NULL); //删除
二:信号:
signal(int sinno, void (*fa)(int));
三:文件操作:
int access(const char *filename); //查看文件是否存在
int open(int fd,int oflag,.../* mode_t mode*/)
ssize_t write(int fd,void *buf,size_t nbytes);
ssize_t read(int fd,void *buf,size_t nbytes);
思想架构:
头文件:
dao.h ----> 定义文件操作的函数
bank.h -----> 定义宏,结构体,设置外部变量 key1 key2
.c文件
client.c ----> 客户端操作 主要包括客户端的输入提示,和把数据传入服务器端
server.c ----> 创建消息队列,启动服务端,用vfork()创建子进程,用execl() 执行open.c 的目标文件。并且设定 SIG_INT 信号,接收此信号时就删除消息队列。
open.c -----> 用来接收客户端传来的消息,并对消息进行业务逻辑操作
dao.c -----> dao.h中一定的函数的具体实现,供open.c 文件调用,主要是用来对文件的操作。
bank.c ----> 此文件主要用来对两个 key 赋值。
感觉有点类似网站建设中的三层架构。
client.c 主要用来负责用户层
open.c 主要用来负责业务逻辑层
dao.c 主要用来负责数据操作层
设计的技术点不多,但是确实个规范的项目。头文件和各个文件的作用值得学习。框架是老师建的,功能都是自己实现的,如有错误还望指正:
bank.h:
1 //客户端和服务器端共用的头文件 2 #ifndef BANK_H_ 3 #define BANK_H_ 4 //声明两个key值,用来给消息队列用,这两个key在另一个文件中定义 5 6 extern const int key1;//客户端向服务器发送消息队列的键值key 7 extern const int key2;//服务器向客户端发送的 8 9 //消息类型 定义为各种宏 方便处理 10 #define M_OPEN 1//代表开户类型 11 #define M_DESTROY 2 //销户 12 #define M_SAVE 3 //存钱 13 #define M_TAKE 4 //取钱 14 #define M_QUERY 5 //查询 15 #define M_TRANSF 6 //转账 16 #define M_SUCCESS 7 //处理成功 17 #define M_FAILED 8 //处理失败 18 19 //包含帐户信息的帐户结构体 20 struct Account{ 21 int id;//帐号 22 char name[10];//用户名 23 char password[10];//密码 24 double balance;//金额 25 }; 26 27 //消息的结构体 28 struct Msg{ 29 long mtype;//消息的类型 30 struct Account acc;//帐户的信息结构体 31 }; 32 #endif
dao.h:
1 //数据对象的存储 2 #ifndef DAO_H_ 3 #define DAO_H_ 4 #include "bank.h" 5 //生成不重复的帐号 6 int generator_id(); 7 //将新帐号添加到文件中,为开户服务 8 int createUser(struct Account acc); 9 //销户功能 10 int destroyUser(struct Account acc); 11 //存钱功能 12 int saveMoney(struct Account acc,struct Account *p); 13 //取钱功能 14 int getMoney(struct Account acc,struct Account *p); 15 //查询余额功能 16 int checkMoney(struct Account acc,struct Account *p); 17 //转账功能 18 int moveMoney(struct Account acc1,struct Account acc2,struct Account *p); 19 #endif
bank.c:
1 //对头文件中的变量进行定义 2 #include "bank.h" 3 4 const int key1 = 0x12345678; 5 const int key2 = 0x23456789;
server.c:
1 //ATM的服务器端 2 #include "bank.h" 3 #include <sys/types.h> 4 #include <sys/ipc.h> 5 #include <sys/msg.h> 6 #include <stdio.h> 7 #include <stdlib.h> 8 #include <unistd.h> 9 #include <signal.h> 10 11 //首先需要创建两个全局存储消息队列的id的变量 12 static int msgid1; 13 static int msgid2; 14 //写一个函数,用来创建两个消息队列,用到msgget函数 15 void init(){ 16 //msgget函数,第一个参数是键值,bank.h中有 17 //IPC_CREAT表示没有此消息队列则创建, 18 //IPC_EXCL表示如果队列存在,则提示错误 19 //0666是给此消息队列对象添加一些权限设置,类似文件系统 20 //创建消息队列1 21 msgid1 = msgget(key1,IPC_CREAT|IPC_EXCL|0666); 22 if(msgid1 == -1){ 23 perror("消息队列1创建失败"),exit(-1); 24 } 25 printf("消息队列1创建成功\n"); 26 //创建消息队列2 27 msgid2 = msgget(key2,IPC_CREAT|IPC_EXCL|0666); 28 if(msgid2 == -1){ 29 perror("消息队列2创建失败"); 30 exit(-1); 31 } 32 printf("消息队列2创建成功\n"); 33 } 34 //启动服务函数,用来创建子进程来执行用户不同的选择的结果 35 void start(){ 36 printf("服务器正在启动..\n"); 37 sleep(2); 38 //创建子进程 39 pid_t open_pid = vfork(); 40 if(open_pid == -1){ 41 perror("vfork failed"); 42 exit(-1); 43 } 44 else if(open_pid == 0){//进入子进程 45 //printf("in child process\n"); 46 execl("./open","open",NULL);//不再和父进程共用代码段 47 exit(-1); 48 } 49 else{ 50 printf("正在等待客户端选择..\n"); 51 waitpid(open_pid,0,0);//阻塞,等待子进程结束 52 //printf("in parent process\n"); 53 } 54 } 55 56 void sig_exit(){ 57 printf("服务器正在关闭..\n"); 58 sleep(1); 59 if(msgctl(msgid1,IPC_RMID,NULL) == -1){ 60 perror("消息队列1删除失败\n"); 61 exit(-1); 62 } 63 else{ 64 printf("消息队列1删除成功\n"); 65 } 66 if(msgctl(msgid2,IPC_RMID,NULL) == -1){ 67 perror("消息队列2删除失败\n"); 68 exit(-1); 69 } 70 else{ 71 printf("消息队列2删除成功\n"); 72 } 73 printf("服务器成功关闭\n"); 74 exit(0);//直接结束程序 75 76 } 77 int main(){ 78 //我们退出服务器用一个信号处理函数来实现 79 printf("退出服务器请按CTRL+C\n");//发送SIGINT信号 80 signal(SIGINT,sig_exit);//自定义信号处理函数,进行退出操作 81 //做好了善后工作,我们从头开始启动服务器 82 //创建消息队列 83 init(); 84 //启动服务 85 86 start(); 87 return 0; 88 }
open.c:
1 /*开户*/ 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <sys/types.h> 5 #include <sys/ipc.h> 6 #include <sys/msg.h> 7 #include "bank.h" 8 int main(){ 9 int msgid1 = msgget(key1,0); 10 if(msgid1 == -1){ 11 perror("获取消息队列1失败"); 12 printf("服务器启动失败"); 13 exit(-1); 14 } 15 int msgid2 = msgget(key2,0); 16 if(msgid2 == -1){ 17 perror("获取消息队列2失败"); 18 printf("服务器启动失败"); 19 exit(-1); 20 } 21 //获取到消息队列之后,开始接受消息 22 while(1){ 23 struct Msg msg;//存储消息信息的结构体 24 struct Account accMove,accResult;//存储帐户信息 25 //首先从客户那里收取消息队列1的信息 msgrcv 函数 26 if(msgrcv(msgid1,&msg,sizeof(msg.acc),0,0) <=0 ){ 27 break; 28 } 29 //如果接受到了消息,根据消息的不同类型进行不同的操作 30 if(msg.mtype == M_OPEN){//如果类型是开户 31 int id = generator_id(); 32 accMove = msg.acc; 33 accMove.id = id; 34 if(createUser(accMove)==-1){ 35 printf("开户失败"); 36 msg.mtype = M_FAILED;//将消息类型置成失败 37 } 38 else{ 39 printf("开户成功"); 40 msg.mtype = M_SUCCESS; 41 } 42 msg.acc.id = id; 43 msgsnd(msgid2,&msg,sizeof(msg.acc),0);//将消息由消息队列2发送给客户端 44 //执行开户的操作 45 //给用户分配帐号,将用户信息记录在文件中。 46 } 47 //最后把消息发过去,这时候消息的类型应该是两种情况M_SUCESS 48 //或者是M_FAILED 49 else if(msg.mtype == M_DESTROY){ 50 accMove = msg.acc; 51 int desval = destroyUser(accMove); 52 if(desval == -1){ 53 printf("没有此ID\n"); 54 msg.mtype = M_FAILED; 55 } 56 else if(desval == 0){ 57 printf("销户失败!\n"); 58 msg.mtype = M_FAILED; 59 } 60 else{ 61 printf("销户成功!\n"); 62 msg.mtype = M_SUCCESS; 63 } 64 if(msgsnd(msgid2,&msg,sizeof(msg.acc),0) == -1) perror("msgsnd"),exit(-1); 65 } 66 else if(msg.mtype == M_SAVE){ 67 accMove = msg.acc; 68 int saval = saveMoney(accMove,&accResult); 69 if(saval == -1){ 70 printf("没有此ID\n"); 71 msg.mtype = M_FAILED; 72 } 73 else if(saval == 0){ 74 printf("存钱失败!\n"); 75 msg.mtype = M_FAILED; 76 } 77 else{ 78 printf("存钱成功!\n"); 79 msg.mtype = M_SUCCESS; 80 } 81 msg.acc = accResult; 82 if(msgsnd(msgid2,&msg,sizeof(msg.acc),0) == -1) perror("msgsnd"),exit(-1); 83 } 84 else if(msg.mtype == M_TAKE){ 85 accMove = msg.acc; 86 int saval = getMoney(accMove,&accResult); 87 if(saval == -1){ 88 printf("没有此ID\n"); 89 msg.mtype = M_FAILED; 90 } 91 else if(saval == 2){ 92 printf("余额不足\n"); 93 msg.mtype = M_FAILED; 94 } 95 else if(saval == 0){ 96 printf("取钱失败!\n"); 97 msg.mtype = M_FAILED; 98 } 99 else{ 100 printf("取钱成功!\n"); 101 msg.mtype = M_SUCCESS; 102 } 103 msg.acc = accResult; 104 if(msgsnd(msgid2,&msg,sizeof(msg.acc),0) == -1) perror("msgsnd"),exit(-1); 105 106 } 107 else if(msg.mtype == M_QUERY){ 108 accMove = msg.acc; 109 int saval = checkMoney(accMove,&accResult); 110 if(saval == -1){ 111 printf("没有此ID\n"); 112 msg.mtype = M_FAILED; 113 } 114 else if(saval == 0){ 115 printf("查询失败!\n"); 116 msg.mtype = M_FAILED; 117 } 118 else{ 119 printf("查询成功!\n"); 120 msg.mtype = M_SUCCESS; 121 } 122 msg.acc = accResult; 123 if(msgsnd(msgid2,&msg,sizeof(msg.acc),0) == -1) perror("msgsnd"),exit(-1); 124 125 } 126 else if(msg.mtype == M_TRANSF){ 127 struct Account accto; 128 accMove = msg.acc; 129 if(msgrcv(msgid1,&msg,sizeof(struct Account),0,0) == -1) perror("msgrcv"),exit(-1); 130 accto = msg.acc; 131 int tra = moveMoney(accMove,accto,&accResult); 132 if(tra == -1){ 133 printf("没有此ID\n"); 134 msg.mtype = M_FAILED; 135 } 136 else if(tra == 0){ 137 printf("转账失败\n"); 138 msg.mtype = M_FAILED; 139 } 140 else if(tra == 2){ 141 printf("余额不足\n"); 142 msg.mtype = M_FAILED; 143 } 144 else{ 145 printf("转账成功\n"); 146 msg.mtype = M_SUCCESS; 147 } 148 msg.acc = accResult; 149 if(msgsnd(msgid2,&msg,sizeof(struct Account),0) == -1) perror("msgsnd"),exit(-1); 150 151 } 152 } 153 return 0; 154 }