大家好,我是ST。
今天主要聊一聊,如何使用Linux系统下的msg实现银行终端系统的vwin 及运行。
第一:msg基本简介
Linux系统中IPC通信有多种方式,msg是其中的一种通信方式,称为消息队列。Linux系统中可以通过ipcs -q来查看所有存在的消息队列。
消息队列与FIFO很类似,都是一个队列结构,并可以实现多进程往队列写入信息,以及多进程可以从队列里面读取信息。但是FIFO需要读写两个端口,事先要打开,这样才能正常传递消息。而消息队列可以事先往队列里面写入消息,或者需要时候再打开,读取消息。
注意:消息队列提供了一个从一个进程往另外一个进程发送数据块的方法,并且每个数据块可以包含多个数据类型,接收的进程可以独立接收不同的数据类型。
第二:Linux内核中msg相关的API函数
#include
第三:银行终端系统的实现原理
补充项:服务器开启时负责创建两个消息队列,退出时销毁所有子进程以及两个消息队列。
第四:银行终端系统的实现步骤
一、程序设计思路
分为两个模块:1.服务器 2.客户端
1.服务器要做的工作就是,在运行服务器的时候让服务器去启动各个进程,开户,存款,查询,销户...,可以使用vfork+execl去实现,(要保证服务器一直在运行不能让服务器退出),由于服务器要启动每一个进程所以采用for循环语句可以定义一个结构体去填写每一个需要启动的进程的路径以及名字execl()函数启动。服务器退出的时候要杀死每一个进程以及删除消息队列。
2.客户端主要实现各个函数功能的调用菜单的编写,采用模块化编程的方式去写代码,每一个模块分别编写一个.c文件这样方便调试和管理,进程之间的通信采用消息队列的方式通信,每一个进程所发的消息可以采用消息队列结构体中的消息类型来区分,将每一个用户的信息都保存成一个文件,方便后期的信息查询,(如果有同学学了数据库可以将每一位用户的信息都保存到数据中)用户在开户的时候,账号如果不想采用顺序分配(本次系统采用的是顺序分配),也可以采用srand((unsigned int)time(NULL))+rand()去进行随机分配。
二、流程图设计
三、编写代码
//服务器 staticint g_reqid = -1;//请求消息队列 static int g_resid = -1;//响应消息队列 typedef struct tag_Service { char srv_path[PATH_MAX+1]; pid_t srv_pid; } SERVICE; static SERVICE g_srv[] = { {"./open", -1}, {"./close", -1}, {"./save", -1}, {"./withdraw", -1}, {"./query", -1}, {"./transfer", -1} }; int init (void) { printf ("服务器初始化... "); //创建请求消息队列 if ((g_reqid = msgget (KEY_REQUEST, 0600 | IPC_CREAT | IPC_EXCL)) == -1) { perror ("msgget"); return -1; } printf ("创建请求消息队列成功! "); //创建响应消息队列 if ((g_resid = msgget (KEY_RESPOND, 0600 | IPC_CREAT | IPC_EXCL)) == -1) { perror ("msgget"); return -1; } printf ("创建响应消息队列成功! "); return 0; } void deinit (void) { printf ("服务器终结化... "); if (msgctl (g_reqid, IPC_RMID, NULL) == -1) perror ("msgctl"); else printf ("销毁请求消息队列成功! "); if (msgctl (g_resid, IPC_RMID, NULL) == -1) perror ("msgctl"); else printf ("销毁响应消息队列成功! "); } int start (void) { printf ("启动业务服务... "); size_t i; //利用for循环的去vfork几个子进程 for (i = 0; i < sizeof (g_srv) / sizeof (g_srv[0]); i++) { if ((g_srv[i].srv_pid = vfork ()) == -1) { perror ("vfork"); return -1; } //调用execl函数去循环的启动每一个可执行的进程 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; } int stop (void) { printf ("停止业务服务... "); size_t i; for (i = 0; i < sizeof (g_srv) / sizeof (g_srv[0]); i++) if (kill (g_srv[i].srv_pid, SIGINT) == -1) { perror ("kill"); return -1; } for (;;) if (wait (0) == -1) { if (errno != ECHILD) { perror ("wait"); return -1; } break; } return 0; } void sigint(int signum) { printf("%d ", signum);//打印出信号量的数值(2) stop();//调用此函数 exit(0); } int main (void) { atexit (deinit);// 注册终止函数(即main执行结束后调用的函数) signal(SIGINT, sigint);//产生ctrl+c信号的时候就去执行sigint函数 if (init () == -1) return -1; if (start () == -1) return -1; sleep (1); printf ("按 <回车> 退出... "); getchar (); if (stop () == -1) return -1; return 0; } 回车>
//头文件bank.h #ifndef _BANK_H #define _BANK_H #include#include #define KEY_REQUEST 0x12345678 #define KEY_RESPOND 0x87654321 #define TYPE_OPEN 8001 #define TYPE_CLOSE 8002 #define TYPE_SAVE 8003 #define TYPE_WITHDRAW 8004 #define TYPE_QUERY 8005 #define TYPE_TRANSFER 8006 //账户 typedef struct tag_ Account { int id;//ID号 char name[256];//用户名 char passwd[9];//密码 double balance;//金额 } ACCOUNT; //开户时的结构体 typedef struct tag_OpenRequest { long type;//类型 pid_t pid; char name[256]; char passwd[9]; double balance; } OPEN_REQUEST; //开户应答结构体 typedef struct tag_OpenRespond { long type; char error[512]; int id; } OPEN_RESPOND; //清户结构体 typedef struct tag_CloseRequest { long type; pid_t pid; int id; char name[256]; char passwd[9]; } CLOSE_REQUEST; //清户应答结构体 typedef struct tag_CloseRespond { long type; char error[512]; double balance; } CLOSE_RESPOND; //存款结构体 typedef struct tag_SaveRequest { long type; pid_t pid; int id; char name[256]; double money; } SAVE_REQUEST; //存款应答结构体 typedef struct tag_SaveRespond { long type; char error[512]; double balance; } SAVE_RESPOND; //取款结构体 typedef struct tag_WithdrawRequest { long type; pid_t pid; int id; char name[256]; char passwd[9]; double money; } WITHDRAW_REQUEST; //取款应答结构体 typedef struct tag_WithdrawRespond { long type; char error[512]; double balance; } WITHDRAW_RESPOND; //查询结构体 typedef struct tag_QueryRequest { long type; pid_t pid; int id; char name[256]; char passwd[9]; } QUERY_REQUEST; //查询应答结构体 typedef struct tag_QueryRespond { long type; char error[512]; double balance; } QUERY_RESPOND; //转账结构体 typedef struct tag_TransferRequest { long type; pid_t pid; int id[2]; char name[2][256]; char passwd[9]; double money; } TRANSFER_REQUEST; //转账应答结构体 typedef struct tag_TransferRespond { long type; char error[512]; double balance; } TRANSFER_RESPOND; #endif // _BANK_H
static int g_reqid = -1; static int g_resid = -1; void menu_loop (int (*menu) (void), int (*on_menu[]) (void), size_t size) { for (;;) { int id = menu (); if (id < 0 || size <= id) printf ("无效选择! "); else if (on_menu[id] () == -1) break; } } int main_menu (void) { printf ("-------- "); printf ("本地银行 "); printf ("-------- "); printf ("[1] 开户 "); printf ("[2] 清户 "); printf ("[3] 存款 "); printf ("[4] 取款 "); printf ("[5] 查询 "); printf ("[6] 转账 "); printf ("[0] 退出 "); printf ("-------- "); printf ("请选择:"); int id = -1; scanf ("%d", &id); scanf ("%*[^ ]"); scanf ("%*c"); return id; } //0 退出 int on_quit (void) { printf ("谢谢使用,再见! "); return -1; } //1 开户 int on_open (void) { pid_t pid = getpid ();//获得该进程号 OPEN_REQUEST req = {TYPE_OPEN, pid};//TYPE_OPEN 8001 printf ("户名:"); scanf ("%s", req.name); printf ("密码:"); scanf ("%s", req.passwd); printf ("金额:"); scanf ("%lf", &req.balance); //把消息添加到已打开的请求消息队列末尾 if (msgsnd (g_reqid, &req, sizeof (req) - sizeof (req.type), 0) == -1) { perror ("msgsnd"); return 0; } OPEN_RESPOND res;//定义一个应答结构体变量 //把消息从响应消息队列中取走 if (msgrcv (g_resid, &res, sizeof (res) - sizeof (res.type), pid, 0) == -1) { perror ("msgrcv"); return 0; } if (strlen (res.error)) { printf ("%s ", res.error); return 0; } printf ("账号:%d ", res.id); return 0; } //2 清户 int on_close (void) { pid_t pid = getpid (); CLOSE_REQUEST req = {TYPE_CLOSE, pid}; printf ("账号:"); scanf ("%d", &req.id); printf ("户名:"); scanf ("%s", req.name); printf ("密码:"); scanf ("%s", req.passwd); if (msgsnd (g_reqid, &req, sizeof (req) - sizeof (req.type), 0) == -1) { perror ("msgsnd"); return 0; } CLOSE_RESPOND res; if (msgrcv (g_resid, &res, sizeof (res) - sizeof (res.type), pid, 0) == -1) { perror ("msgrcv"); return 0; } if (strlen (res.error)) { printf ("%s ", res.error); return 0; } printf ("余额:%.2lf ", res.balance); return 0; } //3 存款 int on_save (void) { pid_t pid = getpid (); SAVE_REQUEST req = {TYPE_SAVE, pid}; printf ("账号:"); scanf ("%d", &req.id); printf ("户名:"); scanf ("%s", req.name); printf ("金额:"); scanf ("%lf", &req.money); if (msgsnd (g_reqid, &req, sizeof (req) - sizeof (req.type), 0) == -1) { perror ("msgsnd"); return 0; } SAVE_RESPOND res; if (msgrcv (g_resid, &res, sizeof (res) - sizeof (res.type), pid, 0) == -1) { perror ("msgrcv"); return 0; } if (strlen (res.error)) { printf ("%s ", res.error); return 0; } printf ("余额:%.2lf ", res.balance); return 0; } //4 取款 int on_withdraw (void) { pid_t pid = getpid (); WITHDRAW_REQUEST req = {TYPE_WITHDRAW, pid}; printf ("账号:"); scanf ("%d", &req.id); printf ("户名:"); scanf ("%s", req.name); printf ("密码:"); scanf ("%s", req.passwd); printf ("金额:"); scanf ("%lf", &req.money); if (msgsnd (g_reqid, &req, sizeof (req) - sizeof (req.type), 0) == -1) { perror ("msgsnd"); return 0; } WITHDRAW_RESPOND res; if (msgrcv (g_resid, &res, sizeof (res) - sizeof (res.type), pid, 0) == -1) { perror ("msgrcv"); return 0; } if (strlen (res.error)) { printf ("%s ", res.error); return 0; } printf ("余额:%.2lf ", res.balance); return 0; } //5 查询 int on_query (void) { pid_t pid = getpid (); QUERY_REQUEST req = {TYPE_QUERY, pid}; printf ("账号:"); scanf ("%d", &req.id); printf ("户名:"); scanf ("%s", req.name); printf ("密码:"); scanf ("%s", req.passwd); if (msgsnd (g_reqid, &req, sizeof (req) - sizeof (req.type), 0) == -1) { perror ("msgsnd"); return 0; } QUERY_RESPOND res; if (msgrcv (g_resid, &res, sizeof (res) - sizeof (res.type), pid, 0) == -1) { perror ("msgrcv"); return 0; } if (strlen (res.error)) { printf ("%s ", res.error); return 0; } printf ("余额:%.2lf ", res.balance); return 0; } //6 转账 int on_transfer (void) { pid_t pid = getpid (); TRANSFER_REQUEST req = {TYPE_TRANSFER, pid}; printf ("账号:"); scanf ("%d", &req.id[0]); printf ("户名:"); scanf ("%s", req.name[0]); printf ("密码:"); scanf ("%s", req.passwd); printf ("金额:"); scanf ("%lf", &req.money); printf ("对方账号:"); scanf ("%d", &req.id[1]); printf ("对方户名:"); scanf ("%s", req.name[1]); if (msgsnd (g_reqid, &req, sizeof (req) - sizeof (req.type), 0) == -1) { perror ("msgsnd"); return 0; } TRANSFER_RESPOND res; if (msgrcv (g_resid, &res, sizeof (res) - sizeof (res.type), pid, 0) == -1) { perror ("msgrcv"); return 0; } if (strlen (res.error)) { printf ("%s ", res.error); return 0; } printf ("余额:%.2lf ", res.balance); return 0; } int main (void) { if ((g_reqid = msgget (KEY_REQUEST, 0)) == -1) {//发送请求消息队列 perror ("msgget"); return -1; } if ((g_resid = msgget (KEY_RESPOND, 0)) == -1) {//获得响应消息队列 perror ("msgget"); return -1; } //定义函数指针数组 int (*on_menu[]) (void) = {on_quit, on_open, on_close, on_save, on_withdraw, on_query, on_transfer}; menu_loop (main_menu, on_menu, sizeof (on_menu) / sizeof (on_menu[0])); return 0; }
//开户子进程(open.c) void sigint (int signum) { printf("开户服务:即将停止。 "); exit (0); } int main (void) { if (signal (SIGINT, sigint) == SIG_ERR) {//产生ctrl+c信号的时候就去执行sigint函数 perror ("signal"); return -1; } //获得消息队列 int reqid = msgget (KEY_REQUEST, 0); if (reqid == -1) { perror ("msgget"); return -1; } //获得相应消息队列 int resid = msgget (KEY_RESPOND, 0); if (resid == -1) { perror ("msgget"); return -1; } printf ("开户服务:启动就绪。 "); for (;;) { OPEN_REQUEST req; if (msgrcv (reqid, &req, sizeof (req) - sizeof (req.type), TYPE_OPEN, 0) == -1) { perror ("msgrcv"); continue; } OPEN_RESPOND res = {req.pid, ""}; ACCOUNT acc; if ((acc.id = genid ()) == -1) { sprintf (res.error, "创立账户失败!"); goto send_respond; } strcpy (acc.name, req.name); strcpy (acc.passwd, req.passwd); acc.balance = req.balance; if (save (&acc) == -1) { sprintf (res.error, "保存账户失败!"); goto send_respond; } res.id = acc.id; send_respond: if (msgsnd (resid, &res, sizeof (res) - sizeof (res.type), 0) == -1) { perror ("msgsnd"); continue; } } return 0; }
//销户子进程(close.c) void sigint (int signum) { printf ("清户服务:即将停止。 "); exit (0); } int main (void) { if (signal (SIGINT, sigint) == SIG_ERR) { perror ("signal"); return -1; } int reqid = msgget (KEY_REQUEST, 0); if (reqid == -1) { perror ("msgget"); return -1; } int resid = msgget (KEY_RESPOND, 0); if (resid == -1) { perror ("msgget"); return -1; } printf ("清户服务:启动就绪。 "); for (;;) { CLOSE_REQUEST req; if (msgrcv (reqid, &, sizeof (req) - sizeof (req.type), TYPE_CLOSE, 0) == -1) { perror ("msgrcv"); continue; } CLOSE_RESPOND res = {req.pid, ""}; ACCOUNT acc; if (get (req.id, &acc) == -1) { sprintf (res.error, "无效账号!"); goto send_respond; } if (strcmp (req.name, acc.name)) { sprintf (res.error, "无效户名!"); goto send_respond; } if (strcmp (req.passwd, acc.passwd)) { sprintf (res.error, "密码错误!"); goto send_respond; } if (delete(req.id) == -1) { sprintf (res.error, "删除账户失败!"); goto send_respond; } res.balance = acc.balance; send_respond: if (msgsnd (resid, &res, sizeof (res) - sizeof (res.type), 0) == -1) { perror ("msgsnd"); continue; } } return 0; }
//存款子进程(save.c) void sigint (int signum) { printf ("清户服务:即将停止。 "); exit (0); } int main (void) { if (signal (SIGINT, sigint) == SIG_ERR) { perror ("signal"); return -1; } int reqid = msgget (KEY_REQUEST, 0); if (reqid == -1) { perror ("msgget"); return -1; } int resid = msgget (KEY_RESPOND, 0); if (resid == -1) { perror ("msgget"); return -1; } printf ("清户服务:启动就绪。 "); for (;;) { CLOSE_REQUEST req; if (msgrcv (reqid, &req, sizeof (req) - sizeof (req.type), TYPE_CLOSE, 0) == -1) { perror ("msgrcv"); continue; } CLOSE_RESPOND res = {req.pid, ""}; ACCOUNT acc; if (get (req.id, &acc) == -1) { sprintf (res.error, "无效账号!"); goto send_respond; } if (strcmp (req.name, acc.name)) { sprintf (res.error, "无效户名!"); goto send_respond; } if (strcmp (req.passwd, acc.passwd)) { sprintf (res.error, "密码错误!"); goto send_respond; } if (delete (req.id) == -1) { sprintf (res.error, "删除账户失败!"); goto send_respond; } res.balance = acc.balance; send_respond: if (msgsnd (resid, &res, sizeof (res) - sizeof (res.type), 0) == -1) { perror ("msgsnd"); continue; } } return 0; }
//取款子进程(withdraw.c) void sigint (int signum) { printf ("取款服务:即将停止。 "); exit (0); } int main (void) { if (signal (SIGINT, sigint) == SIG_ERR) { perror ("signal"); return -1; } int reqid = msgget (KEY_REQUEST, 0); if (reqid == -1) { perror ("msgget"); return -1; } int resid = msgget (KEY_RESPOND, 0); if (resid == -1) { perror ("msgget"); return -1; } printf ("取款服务:启动就绪。 "); for (;;) { WITHDRAW_REQUEST req; if (msgrcv (reqid, &req, sizeof (req) - sizeof (req.type), TYPE_WITHDRAW, 0) == -1) { perror ("msgrcv"); continue; } WITHDRAW_RESPOND res = {req.pid, ""}; ACCOUNT acc; if (get (req.id, &acc) == -1) { sprintf (res.error, "无效账号!"); goto send_respond; } if (strcmp (req.name, acc.name)) { sprintf (res.error, "无效户名!"); goto send_respond; } if (strcmp (req.passwd, acc.passwd)) { sprintf (res.error, "密码错误!"); goto send_respond; } if (req.money > acc.balance) { sprintf (res.error, "余额不足!"); goto send_respond; } acc.balance -= req.money; if (update (&acc) == -1) { sprintf (res.error, "更新账户失败!"); goto send_respond; } res.balance = acc.balance; send_respond: if (msgsnd (resid, &res, sizeof (res) - sizeof (res.type), 0) == -1) { perror ("msgsnd"); continue; } } return 0; }
//查询子进程(query.c) void sigint (int signum) { printf ("查询服务:即将停止。 "); exit (0); } int main (void) { if (signal (SIGINT, sigint) == SIG_ERR) { perror ("signal"); return -1; } int reqid = msgget (KEY_REQUEST, 0); if (reqid == -1) { perror ("msgget"); return -1; } int resid = msgget (KEY_RESPOND, 0); if (resid == -1) { perror ("msgget"); return -1; } printf ("查询服务:启动就绪。 "); for (;;) { QUERY_REQUEST req; if (msgrcv (reqid, &req, sizeof (req) - sizeof (req.type), TYPE_QUERY, 0) == -1) { perror ("msgrcv"); continue; } QUERY_RESPOND res = {req.pid, ""}; ACCOUNT acc; if (get (req.id, &acc) == -1) { sprintf (res.error, "无效账号!"); goto send_respond; } if (strcmp (req.name, acc.name)) { sprintf (res.error, "无效户名!"); goto send_respond; } if (strcmp (req.passwd, acc.passwd)) { sprintf (res.error, "密码错误!"); goto send_respond; } res.balance = acc.balance; send_respond: if (msgsnd (resid, &res, sizeof (res) - sizeof (res.type), 0) == -1) { perror ("msgsnd"); continue; } } return 0; }
//转账子进程(transfer) void sigint (int signum) { printf ("转账服务:即将停止。 "); exit (0); } int main (void) { if (signal (SIGINT, sigint) == SIG_ERR) { perror ("signal"); return -1; } int reqid = msgget (KEY_REQUEST, 0); if (reqid == -1) { perror ("msgget"); return -1; } int resid = msgget (KEY_RESPOND, 0); if (resid == -1) { perror ("msgget"); return -1; } printf ("转账服务:启动就绪。 "); for (;;) { TRANSFER_REQUEST req; if (msgrcv (reqid, &req, sizeof (req) - sizeof (req.type), TYPE_TRANSFER, 0) == -1) { perror ("msgrcv"); continue; } TRANSFER_RESPOND res = {req.pid, ""}; ACCOUNT acc[2]; if (get (req.id[0], &acc[0]) == -1) { sprintf (res.error, "无效账号!"); goto send_respond; } if (strcmp (req.name[0], acc[0].name)) { sprintf (res.error, "无效户名!"); goto send_respond; } if (strcmp (req.passwd, acc[0].passwd)) { sprintf (res.error, "密码错误!"); goto send_respond; } if (req.money > acc[0].balance) { sprintf (res.error, "余额不足!"); goto send_respond; } if (get (req.id[1], &acc[1]) == -1) { sprintf (res.error, "无效对方账号!"); goto send_respond; } if (strcmp (req.name[1], acc[1].name)) { sprintf (res.error, "无效对方户名!"); goto send_respond; } acc[0].balance -= req.money; if (update (&acc[0]) == -1) { sprintf (res.error, "更新账户失败!"); goto send_respond; } acc[1].balance += req.money; if (update (&acc[1]) == -1) { sprintf (res.error, "更新对方账户失败!"); goto send_respond; } res.balance = acc[0].balance; send_respond: if (msgsnd (resid, &res, sizeof (res) - sizeof (res.type), 0) == -1) { perror ("msgsnd"); continue; } } return 0; }
//封装的库(lib.c) const char* ID_FILE = "id.dat"; int genid (void) { int id = 1000; int fd = open (ID_FILE, O_RDWR | O_CREAT, 0644); if (fd == -1) { perror ("open"); return -1; } if (read(fd, &id, sizeof (id)) == -1) { perror ("read"); return -1; } id++; if (lseek (fd, 0, SEEK_SET) == -1) { perror ("lseek"); return -1; } if (write (fd, &id, sizeof (id)) == -1) { perror ("write"); return -1; } close (fd); return id; } int save (const ACCOUNT* acc) { char pathname[PATH_MAX+1]; sprintf (pathname, "%d.acc", acc -> id); int fd = creat (pathname, 0644); if (fd == -1) { perror ("creat"); return -1; } if (write (fd, acc, sizeof (*acc)) == -1) { perror ("write"); return -1; } close (fd); return 0; } int get (int id, ACCOUNT* acc) { char pathname[PATH_MAX+1]; sprintf (pathname, "%d.acc", 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 update (const ACCOUNT* acc) { char pathname[PATH_MAX+1]; sprintf (pathname, "%d.acc", acc -> id); int fd = open (pathname, 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 delete (int id) { char pathname[PATH_MAX+1]; sprintf (pathname, "%d.acc", id); if (unlink (pathname) == -1) { perror ("unlink"); return -1; } return 0; }
//库的声明(lib.h) #ifndef _LIBFUNC_H #define _LIBFUNC_H #include "../inc/bank.h" int genid (void); int save (const ACCOUNT* acc); int get (int id, ACCOUNT* acc); int update (const ACCOUNT* acc); int delete (int id); #endif // _LIBFUNC_H
总结:Linux系统中,msg消息队列在多进程间通信的使用过程,在实际开发中应用是非常广泛的,十分建议初学Linux编程的工程师们去练习这套系统,加深消息队列在多进程间通信的应用,这将具有很大的益处。
评论
查看更多