清明假期三天没出寝室的门,先是把独立的博客折腾好了。域名备案还没好。域名是ilovecpp.com,意为“我爱C++”,好羞涩,掩面,逃:)。话说cnblogs.com的界面好丑 。其余大部分时间就是折腾这个小项目了,Unix 内核函数各种结构、flags即使查man手册还是看的头大。

  用消息而不是Socket,是因为最近看Unix C比较多,于是写le这个小项目(玩具),更好的了解UNIX的消息机制。

  

  1,测试时,server端异常退出时消息队列没有及时的删除,之后client端收到的消息总是有偏差,最后调试好久用ipcs才发现消息队列里边还有内容。

  2,由于调用函数比较频繁,调试时发现__LINE__宏挺好用的,可以很快的找到bug。 

  代码:

  一,公共部分包括定义的 宏和key,消息结构,账户结构。

  1.bank.h

  

 //定义消息类型的宏 ,声明产生message的key
//key的定义放在 bank.c 中
#ifndef _BANK_H
#define _BANK_H
#include "acstruct.h"
//key_t 头文件
#include <sys/ipc.h> extern const key_t key1;
extern const key_t key2; //客户端消息类型
#define CT_OPEN 1//开户
#define CT_DELT 2//消户
#define CT_SAVE 3//存钱
#define CT_DRAW 4//取钱
#define CT_QURY 5//查询
#define CT_TRNS 6//转帐
#define CT_LOGN 7//登陆 //服务端消息
#define SV_SUCS 8//操作成功
#define SV_FAIL 9//操作失败 // 保存退出时信号发送附加的数据
//两个 msgid
typedef struct Msg_id {
int msgid1;
int msgid2;
} Msg_id; #endif

  bank.c

 #include "bank.h"

 const key_t key1 = 0x20150405;
const key_t key2 = 0x20150406;

 

  account.h

 // 声明账户结构 和 消息结构
#ifndef _ACSTRUCT_H
#define _ACSTRUCT_H typedef struct {
int id;
char name[];
char pwd[];
double money;
} Account; typedef struct {
long mtype;
Account data;
} MSG; #endif

  二、服务端

  1.server.c

  signal(SIGINT,sigq_send);
  sigaction(SIGUSR1,&act,NULL);

  完全可以只用SIGINT就可以了,我是为了使用sigqueue发送数据,以及使用sigaction操作信号。

  

  收到SIGINT信号时会调用sigq_send,sigq_send调用sigqueue发送SIGUSR1,和数据sigvalue(结构),然后通过sigaction结构的sa_sigaction函数拿到

  info->si_ptr转换一下指针类型就拿到两个msgid了,就可以删除消息队列了。

  我水平臭,写的比较绕。

 //服务端
#include "bank.h"
#include "server.h"
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h> extern const key_t key1;
extern const key_t key2; union sigval sigvalue; int msgid1,msgid2; int main()
{
printf(STAR);
printf(QUIT);
printf(STAR); //初始化服务器,两个传出参数,返回msgid
initserver(&msgid1,&msgid2); //信号绑定,用sigaction()
//不用signal因为要传递两个参数,msgid1,msgid2
Msg_id msg_id;
msg_id.msgid1 = msgid1;
msg_id.msgid2 = msgid2; sigvalue.sival_ptr = &msg_id;
struct sigaction act = {};
act.sa_handler = NULL;
act.sa_sigaction = endserver;
//使用 sa_sigaction 指针
act.sa_flags = SA_SIGINFO; signal(SIGINT,sigq_send);
sigaction(SIGUSR1,&act,NULL); // 接收客户端消息
RcvMsg();
}

  2.server_func.c

 #include "bank.h"
#include "acstruct.h"
#include "server.h"
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdbool.h> extern union sigval sigvalue; extern const key_t key1;
extern const key_t key2; extern int msgid1,msgid2; //服务器初始化
//1.新建两个消息队列,接受和发送消息
void initserver(int* m1,int* m2) {
printf("服务器启动,正在新建消息队列...\n");
sleep();//模拟等待 = =!
int msgid1 = msgget(key1,IPC_CREAT|);
if(msgid1 == -) {
printf("消息队列1创建失败!\n");
exit(-);
}
int msgid2 = msgget(key2,IPC_CREAT|);
if(msgid2 == -) {
printf("消息队列2创建失败!\n");
exit(-);
}
*m1 = msgid1;
*m2 = msgid2;
printf("发送和接受消息队列创建成功!\n");
} //关闭
void end(int sig)
{
exit();
} //接受消息
void RcvMsg() {
MSG msg1;
printf("正在等待客户端请求!\n");
pid_t pid= vfork();
if(pid == -) {
printf("vfork %m\n");
exit(-);
}
esle if(pid == ) {
while() {
signal(SIGUSR1,end);
//type==-7 接受所有客户端发来的消息(1-7)
int res = msgrcv(msgid1,&msg1,sizeof(msg1.data),-,);
if(res == -){
perror("接受客户端消息失败!");
exit(-);
}
DealMsg(msg1);
}
}
else {
waitpid(pid,,);
printf("处理结束!\n");
}
} //处理受到的消息
void DealMsg(MSG msg)
{
switch(msg.mtype) {
case :OpenAct(msg);
break;
case :DelAct(msg);
break;
case :SaveMny(msg);
break;
case :DrawMny(msg);
break;
case :QuryMny(msg);
break;
case :CheckAct(msg);
break;
}
} //关闭服务器,删除消息队列
void sigq_send(int sig) {
sigqueue(getpid(),SIGUSR1,sigvalue);
} void endserver(int sig,siginfo_t* info,void* p)
{
int msgid1,msgid2;
msgid1 = ((Msg_id*)(info->si_ptr))->msgid1;
msgid2 = ((Msg_id*)(info->si_ptr))->msgid2;
printf("正在关闭服务器,删除msgqueue!\n");
int m1 = msgctl(msgid1,IPC_RMID,NULL);
if(m1 == -) {
perror("msgid1");
exit(-);
}
int m2 = msgctl(msgid2,IPC_RMID,NULL);
if(m2 == -) {
perror("msgid2");
exit(-);
}
printf("删除消息队列成功!\n");
printf("正常退出!\n");
exit();
} //创建ID对应的文件,写入账户信息
void OpenAct(MSG msg) {
//先读取可用的ID
int fd = open("Data/id.dat",O_RDWR);
if(fd == -) {
printf("打开ID文件失败!\n");
exit(-);
}
int id,oldid;
read(fd,&id,); //客户端查询时都是提交的id作为参数,不然会找不到文件
//用之前的没写这行创建的帐号找bug,找了好久好久
//吃一堑长一智 只能这样安慰自己 T_T
msg.data.id = id; oldid = id;//要传递给客户端
const char* path = FindPath(id);
//id+1 存入 id.dat
++id;
lseek(fd,,SEEK_SET);
write(fd,&id,);
close(fd);
fd = open(path,O_CREAT|O_RDWR,);
if(fd == -) {
printf("创建ID文件失败!\n");
exit(-);
}
printf("创建id文件成功,正在写入账户信息...\n");
sleep();
printf("%d,%s,%s,%lf",msg.data.id,msg.data.name,msg.data.pwd,msg.data.money);
int r = write(fd,&msg,sizeof(msg));
if(r==-){
printf("写入账户信息到文件失败!\n");
exit(-);
}
close(fd);
msg.mtype = SV_SUCS;
msg.data.id = oldid;
SndBack(msg);
printf("客户端开户请求已经通过,成功返回给客户端!\n");
} //返回处理结果给客户端
void SndBack(MSG msg)
{
int r = msgsnd(msgid2,&msg,sizeof(msg.data),);
if(r == -) {
printf("返回消息给客户端时失败T_T!\n");
exit(-);
}
} //根据id找到,信息文件的路径
const char* FindPath(int id)
{
//这两行不能写成
//static char path[15] ={"Data/"};
//否则 path会变长
static char path[] ={};
strcpy(path,"Data/"); char buf[] = {"0000.id"};
ctos(buf,id);
strcat(path,buf);
return path;
} //检查客户端的登陆的id和密码
void CheckAct(MSG msg){
MSG msg1 = ReadMsg(msg.data.id);
if(!strncmp(msg.data.pwd,msg1.data.pwd,)) {
msg.mtype = SV_SUCS;
SndBack(msg);
printf("客户端登陆密码正确,等待服务请求...\n");
}
else {
msg.mtype = SV_FAIL;
SndBack(msg);
printf("客户端登陆密码错误。\n");
}
} //删除账户
void DelAct(MSG msg) {
const char* path = FindPath(msg.data.id);
int res = remove(path);
if(res==-) {
msg.mtype =SV_FAIL;
SndBack(msg);
exit(-);
}
else {
msg.mtype =SV_SUCS;
SndBack(msg);
printf("%d的信息文件已经删除,账户已销户!\n",msg.data.id);
//此时客户端收到SV_SUCS消息就会关闭。
printf("等待下一个客户端...\n");
}
} void SaveMny(MSG msg){
MSG msg1 = ReadMsg(msg.data.id);
msg1.data.money += msg.data.money;
WriteMsg(msg1);
msg1.mtype =SV_SUCS;
SndBack(msg1);
} void DrawMny(MSG msg) {
MSG msg1 = ReadMsg(msg.data.id);
//判断是否大于存款
if(msg1.data.money < msg.data.money) {
msg1.mtype =SV_FAIL;
SndBack(msg1);
printf("等待下一个客户端请求!\n");
}
else {
msg1.data.money -= msg.data.money;
WriteMsg(msg1);
msg1.mtype =SV_SUCS;
SndBack(msg1);
}
} void QuryMny(MSG msg) {
msg = ReadMsg(msg.data.id);
msg.mtype =SV_SUCS;
SndBack(msg);
printf("查询余额结果已经返回!\n");
} //读取账户文件的信息
MSG ReadMsg(int id){
MSG msg = {};
msg.data.id =id;
const char* path = FindPath(id);
int fd = open(path,O_RDWR);
if(fd == -) {
msg.mtype =SV_FAIL;
SndBack(msg);
exit(-);
}
else{
read(fd,&msg,sizeof(msg));
close(fd);
}
return msg;
} void WriteMsg(MSG msg) {
const char* path = FindPath(msg.data.id);
int fd = open(path,O_RDWR);
if(fd == -) {
msg.mtype =SV_FAIL;
SndBack(msg);
exit(-);
}
else {
write(fd,&msg,sizeof(msg));
close(fd);
}
} //数字id转成字符串,方便建立文件
void ctos(char* buf,int id) {
int i=;
for(;i<;++i) {
buf[-i] = id% + '';
id /= ;
}
}

  三、客户端

  1.client

  

 //客户端
#include "client.h" int msgid1,msgid2; int main()
{
//获取msgid
Getmid(&msgid1,&msgid2); //显示 client login 界面
Login(); }

  2.client_func.c

Unix C给我的感觉就是头文件太多了。

 #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <stdbool.h>
#include <sys/msg.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <string.h>
#include "bank.h"
#include "acstruct.h"
#include "client.h" extern int msgid1,msgid2; void Getmid(int* p1,int* p2) {
int msgid1,msgid2;
msgid1 = msgget(key1,IPC_CREAT|);
if(msgid1 == -) {
printf("获取msgid1失败!\n");
exit(-);
}
msgid2 = msgget(key2,IPC_CREAT|);
if(msgid2 == -) {
printf("获取msgid2失败!\n");
exit(-);
}
*p1 = msgid1;
*p2 = msgid2;
} void initclient(int id) {
int choice;
int iscontinue = ;
while(iscontinue){
printf(LINE);
printf(CHO1);
printf(CHO2);
printf(CHO3);
printf(ENDL);
printf(SELC);
scanf("%d",&choice);
switch(choice) {
case :iscontinue=;
break;
case :DelAct(id);
break;
case :SaveMny(id);
break;
case :DrawMny(id);
break;
case :QuryMny(id);
break;
case :TrnsMny(id);
break;
default:printf("没有这个选项!\n");
break;
}
}
printf("谢谢使用,再见!\n");
} void Login() {
printf(LINE);
printf(CHO5);
printf(ENDL);
printf("退出请按CRTL+C\n");
printf(SELC);
int choice;
scanf("%d",&choice);
switch(choice) {
case :CheckAct();
break;
case :CreatAct();
break;
default:printf("没有这个选项!\n");
exit(-);
}
} void CreatAct() {
Account account = {};
bool flag = false;
bool isfst = true;
char tmp[];
while(!flag) {
printf("%s",isfst?"请输入姓名:":"两次密码不一致,请重新输入姓名:");
scanf("%s",account.name);
printf("请设置密码(前6位有效):");
scanf("%s",account.pwd);
printf("请确认密码:");
scanf("%s",tmp);
if(!strncmp(tmp,account.pwd,))
flag = true;
isfst = false;
}
printf("输入有效,请输入存款金额:");
scanf("%lf",&account.money);
MSG msg={CT_OPEN,account};
printf("正在为您开户...\n");
MSG bkres = SendMsg(&msg);
if(bkres.mtype) {
printf("开户成功,您的ID是%d,这是登陆凭据,请牢记!\n",bkres.data.id);
printf("正在进入ATM服务系统...\n");
initclient(bkres.data.id);
}
else {
printf("开户失败!\n");
exit(-);
}
} MSG SendMsg(MSG* pMsg) {
MSG bkres = {};//保存服务器处理结果
int r = msgsnd(msgid1,pMsg,sizeof(pMsg->data),);
if(r == -) {
printf("发送给服务器的请求失败了T_T!\n");
exit(-);
}
//mtype==-9 接受 消息 类型为 8-9的消息
//头文件内只定义了这两个
r = msgrcv(msgid2,&bkres,sizeof(bkres),-,);
if(r== -) {
printf("接受服务器处理结果失败!\n");
exit(-);
}
return bkres;
} void CheckAct(){
signal(SIGQUIT,CheckAct);
MSG msg;
msg.mtype = CT_OPEN;
int id;
char pwd[]= {};
printf("\n请输入的卡号:");
scanf("%d",&id);
printf("你输入的卡号是%d,",id);
printf("如需重新输入,请按CRTL+‘\\’\n");
printf("现在请输入密码:");
scanf("%s",pwd);
Account account = {};
account.id = id;
strcpy(account.pwd,pwd);
msg.mtype = CT_LOGN;
msg.data = account;
MSG bkres = SendMsg(&msg);
if(bkres.mtype == SV_SUCS) {
printf("密码正确登陆成功!\n");
initclient(id);
}
else {
printf("密码有误,登陆失败!\n");
exit(-);
}
} void DelAct(int id) {
MSG msg;
msg.data.id = id;
msg.mtype = CT_DELT;
MSG bkres = SendMsg(&msg);
if(bkres.mtype == SV_SUCS) {
printf("账户已注销,您已被迫退出系统!\n");
exit();
}
else {
printf("删除失败,请联系客服人员!\n");
exit(-);
}
} void SaveMny(int id) {
double save = ;
while(save <= ) {
printf("请输入存款金额:");
scanf("%lf",&save);
}
MSG msg;
msg.data.id = id;
msg.data.money = save;
msg.mtype = CT_SAVE;
printf("%d\n",msg.data.id);
MSG bkres = SendMsg(&msg);
printf("%d\n",bkres.mtype);
if(bkres.mtype == SV_SUCS)
printf("存款成功!\n");
else {
printf("存款失败,请联系客服人员!\n");
exit(-);
}
} void DrawMny(int id){
double draw = ;
while(draw <= ) {
printf("请输入取款金额:");
scanf("%lf",&draw);
}
MSG msg;
msg.data.id = id;
msg.data.money = draw;
msg.mtype = CT_DRAW;
MSG bkres = SendMsg(&msg);
if(bkres.mtype == SV_SUCS)
printf("取款成功!\n");
else {
printf("没钱取你妹!\n");
exit(-);
}
} double QuryMny(int id) {
double mny=;
MSG msg;
msg.data.id = id;
msg.mtype = CT_QURY;
MSG bkres = SendMsg(&msg);
if(bkres.mtype == SV_SUCS) {
printf("你的余额:%lf\n",bkres.data.money);
mny = bkres.data.money;
}
else {
printf("查询失败!\n");
exit(-);
}
return mny;
} void TrnsMny(int id) {
int destid=;
printf("请输入转帐的ID:");
scanf("%d",&destid);
if(!ActExist(destid)) {
printf("不存在这个账户!\n");
exit(-);
}
double money = ;
while(money <= ) {
printf("输入要转的金额:");
scanf("%lf",&money);
}
if(QuryMny(id) < money) {
printf("你自己的钱都不够呢!\n");
exit(-);
}
else {
Change(id,-money);
Change(destid,money);
printf("转帐成功!\n");
}
} void Change(int id,double money) {
MSG msg;
msg.data.id = id;
msg.data.money = money;
msg.mtype = CT_SAVE;
MSG bkres = SendMsg(&msg);
} //转帐时检测,目标账户是否存在
int ActExist(int id) {
MSG msg;
msg.data.id = id;
msg.mtype = CT_QURY;
MSG bkres = SendMsg(&msg);
if(bkres.mtype == SV_SUCS)
return ;
else
return ;
}

用UNIX消息队列实现IPC(以ATM为例)的更多相关文章

  1. 4、网络并发编程--僵尸进程、孤儿进程、守护进程、互斥锁、消息队列、IPC机制、生产者消费者模型、线程理论与实操

    昨日内容回顾 操作系统发展史 1.穿孔卡片 CPU利用率极低 2.联机批处理系统 CPU效率有所提升 3.脱机批处理系统 CPU效率极大提升(现代计算机雏形) 多道技术(单核CPU) 串行:多个任务依 ...

  2. 消息队列,IPC机制(进程间通信),生产者消费者模型,线程及相关

    消息队列 创建 ''' Queue是模块multiprocessing中的一个类我们也可以这样导入from multiprocessing import Queue,创 建时queue = Queue ...

  3. 【windows 操作系统】进程间通信(IPC)简述|无名管道和命名管道 消息队列、信号量、共享存储、Socket、Streams等

    一.进程间通信简述 每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进 ...

  4. Windows进程间通讯(IPC)----消息队列

    消息队列 windows系统是通过消息驱动的,每移动一下鼠标,点击一下屏幕都会产生一个消息.这些消息会先被放在windows的一个系统消息队列(先进先出)中,windows系统会为每一个GUI线程创建 ...

  5. RT-thread内核之消息队列

    一.消息队列控制块:在include/rtdef.h中 #ifdef RT_USING_MESSAGEQUEUE /** * message queue structure */ struct rt_ ...

  6. Linux 进程间通信 消息队列

    1.特点: 消息队列是IPC对象的一种 消息队列由消息队列ID来唯一标识 消息队列就是一个消息的列表.用户可以在消息队列中添加消息.读取消息等. 消息队列可以按照类型来发送/接收消息(消息的类型是正整 ...

  7. Web应用中的轻量级消息队列

    Web应用中为什么会需要消息队列?主要原因是由于在高并发环境下,由于来不及同步处理,请求往往会发生堵塞,比如说,大量的insert,update之类的请求同时到达mysql,直接导致无数的行锁表锁,甚 ...

  8. Unix IPC之Posix消息队列(1)

    部分参考:http://www.cnblogs.com/Anker/archive/2013/01/04/2843832.html IPC对象的持续性:http://book.51cto.com/ar ...

  9. UNIX IPC: POSIX 消息队列 与 信号

    POSIX消息队列可以注册空队列有消息到达时所触发的信号,而信号触发对应的信号处理函数. 下面是一份基本的消息队列和信号处理结合的代码(修改自UNIX网络编程:进程间通信) #include < ...

随机推荐

  1. 【ASP.NET MVC 】让@Ajax.ActionLink获取的数据不进Cache

    刚玩这个东西的时候,发现IE会进Cache,不管怎么删除,修改,后台删除了,前台还是一样,找了一下,HTML5只提供了 <meta http-equiv="pragma" c ...

  2. sql跨数据库转移

    结构一样的话insert into 数据库A.dbo.TableAselect * from 数据库B.dbo.TableA 另外:nsert into DDD(字段1,字段2,字段3 .....)( ...

  3. string.join加引号

    columnsGen = string.Join(",", modelDictionary.Keys); valueGen = modelDictionary.Values.Agg ...

  4. 【BZOJ 4551】【TJOI2016】【HEOI2016】树

    http://www.lydsy.com/JudgeOnline/problem.php?id=4551 题目描述 给定一棵有根树(根为 1),有以下两种操作:1. 标记操作:对某个结点打上标记(在最 ...

  5. 容器--Collection和AbstractCollection

    一.前言 容器是JAVA中比较重要的一块,整个体系设计得非常好,同时对于代码学习来说也是比较好的范例.同时很多面试官也比较喜欢用容器来考察面试者的基础知识,所以掌握好容器还是比较重要的.本文主要总结一 ...

  6. 不是语言之争--Go vs Erlang

    因为 云巴 系统对高并发.低延迟的需求,我们对各个语言.平台做了很多的调研比较工作.这自然就包括致力于开发高并发应用的 Go 和 Erlang. 并发 Go 对高并发的支持通过 goroutine 实 ...

  7. jvisualvm远程监控jvm设置

    有些时候,需要对特定环境中的Java应用进行实时性能分析,大部分非开发和测试环境,一般都是用jvisualvm进行基本检测以最小化对系统的影响(其开启后,负载影响大约20%-30%),jvisualv ...

  8. C#中List<T>对象的深度拷贝问题

    一.List<T>对象中的T是值类型的情况(int 类型等) 对于值类型的List直接用以下方法就可以复制: List<T> oldList = new List<T&g ...

  9. js事件绑定

    事件绑定,常见的是odiv.onclick=function(){..........};  这种方式绑定事件太单一,如果绑定多个,那么最后一个事件会覆盖掉之前的,也就是说只执行最后一次绑定的事件,这 ...

  10. C#知识点总结【2】

    此文章只是 记录在C#当中一些我个人认为比较重要的知识点,对于有些基础实现或者用法并未说明: 继承 C#当中可以实现两种继承方式 1.实现继承:一个类派生于一个类,它拥有基类的所有成员字段和函数. 2 ...