采用QSharedMemory实现多进程间的通信 Linux | window

直接上代码:简单的很

  1 /////////////////////////////////////////////////////////////////////
  2 /// file: ProcessCommunicate.h
  3 /// Description:本文采用<QSharedMemory>实现同一台电脑上多个进程间的通信。使用极其的简单,你只需要调用一个无参数函数就能完成。
  4 ///
  5 /// Copyright: 成都中科希奥科技有限公司 Official Website:https://siotech.net/
  6 /// Author: 张洪铭-小熊博(zhm-xxbs) Email:zhm_xxbs@163.com
  7 /// Date: 2022-05-17
  8 ///
  9 /////////////////////////////////////////////////////////////////////
 10 /* 使用样例(伪代码)://////////////////////////////////////////////////
 11  * ...
 12  * 配置4个全局变量的初始值(非常简单),搜索:Lable:[Change]
 13  * ...
 14  * std::deque<DisplayMessage>::sendDisplayMsg //把需要发送的消息压入队列
 15  *
 16  * updateMessageDeque(); //仅仅调用这一个函数
 17  *
 18  * std::deque<DisplayMessage>::recvDisplayMsg //从接收消息队列中取出消息来使用
 19  * ...
 20  * 完整样例代码,有界面可直观操作来体验:请联系作者。
 21 ///////////////////////////////////////////////////////////////////*/
 22 
 23 #ifndef BASE_COMMON_H
 24 #define BASE_COMMON_H
 25 
 26 #include <bit>
 27 #include <map>
 28 #include <string>
 29 #include <assert.h>
 30 #include <memory>
 31 #include <deque>
 32 #include <set>
 33 #include <tuple>
 34 #include <list>
 35 #include <iostream>
 36 
 37 #include <QSharedMemory>
 38 
 39 /////////////////////////////////////////////////////////////////////
 40 /// 进程ID + 进程名称 + 管理者ID + 管理者属性
 41 /*
 42  *宏定义进程的名称
 43 */
 44 #define ProcessOne      ("ProcessOne(as manager)")
 45 #define ProcessTwo      ("ProcessTwo  ")
 46 #define ProcessThree    ("ProcessThree")
 47 
 48 /*
 49  * variable: Id_ProcessName
 50  * desc: 所有伙伴进程的ID编号和进程名称的对应关系映射
 51  * Lable:[Change]
 52 */
 53 extern std::map<unsigned char, std::string> ID_ProcessName;
 54 
 55 /*
 56  * variable: ID_Ower
 57  * desc: 进程自己的ID编号
 58  * Lable:[Change]
 59 */
 60 static constexpr unsigned char ID_Owner = 0;
 61 
 62 /*
 63  * variable: ID_Manager
 64  * desc: 管理者的进程编号
 65  * Lable:[Change]
 66 */
 67 static constexpr unsigned char ID_Manager = 0;
 68 
 69 /*
 70  * variable: Is_Manager
 71  * desc: 进程自己是不是管理着进程
 72  * tips:
 73  *      1.当一个伙伴群体中,只安排一个管理者;
 74  *      2.ture:管理者; false:伙伴;
 75  *      Lable:[Change]
 76 */
 77 static constexpr bool Is_Manager = true;
 78 
 79 /////////////////////////////////////////////////////////////////////
 80 /// 公用的常量值
 81 /*
 82  * variable: SharedMemoryReadWriteCycle
 83  * desc: 每个进程读写共享内存的周期。 单位:毫秒;
 84 */
 85 constexpr unsigned SharedMemoryReadWriteCycle = 500;//200;
 86 
 87 /*
 88  * variable: HeartBeatReportCycle
 89  * desc: 每个进程上报自己心跳的周期。 单位:毫秒;
 90 */
 91 constexpr unsigned HeartBeatReportCycle = 5*1000;
 92 
 93 /*
 94  * variable: JudgePartnerOfflineMaxCount
 95  * desc: 判断伙伴进程已经离线,未读取到伙伴消息的最大次数计数值。
 96  * Tips:
 97  *      1. 当计数值达到 JudgePartnerOfflineMaxCount 时,认为伙伴进程已离线;
 98  *      2. 读取消息的周期是 SharedMemoryReadWriteCycle, 因此要确保此值是合理的。
 99 */
100 constexpr unsigned char JudgePartnerOfflineMaxCount =\
101         (HeartBeatReportCycle/SharedMemoryReadWriteCycle)*3; //3次本该收到心跳,但未收到,则认为离线
102 
103 /*
104  * enum : MsgType
105  * desc : 枚举所有的消息类型
106  *
107  * MsgType_Invalid:非法消息;
108  * MsgType_Display:显示消息;
109  * MsgType_Control:控制消息;
110 */
111 enum MsgType{MsgType_Invalid = 0, MsgType_Display, MsgType_Control};
112 
113 /*
114  * enum : SendType
115  * desc : 枚举所有的发送类型
116  *
117  * SendType_Invalid:非法消息;
118  * SendType_PointToPoint:点对点;
119  * SendType_Broadcast:广播;
120  *
121  * Tips: 目前只有“心跳”属于广播, 其他均为“点对点”;
122 */
123 enum SendType{SendType_Invalid = 0, SendType_PointToPoint, SendType_Broadcast};
124 
125 /*
126  * enum : ControlMsgType
127  * desc : 枚举所有的控制消息的类型
128  *
129  * [0-100]为心跳消息,代表发送心跳进程的ID
130  * ControlMsgType_Invalid:非法控制消息;
131  * ControlMsgType_Bell:响铃;
132  * ControlMsgType_Exit:退出程序;
133 */
134 enum ControlMsgType{/*[0-100]*/ControlMsgType_Invalid = 101, ControlMsgType_Bell, ControlMsgType_Exit};
135 /*
136  * variable: ProcessCommunicationSharedMemory
137  * desc: 伙伴进程间通信使用的共享内存的名称
138 */
139 constexpr char ProcessCommunicationSharedMemory[] = "ProcessCommunicationSharedMemory";
140 
141 /*
142  * variable: NonMessage
143  * desc: 共享内存中没有消息内容项;
144 */
145 constexpr unsigned short NonMessage = -1;
146 
147 /*
148  * variable: MessageMaxJumpNumber
149  * desc: 消息的最大活跃跳数;
150  * tips: 消息的跳数仅由管理者维护;
151 */
152 constexpr unsigned char MessageMaxJumpNumber = 20;
153 
154 /*
155  * variable: HeartbeatMessage
156  * desc: 心跳消息;
157  * tips: 心跳消息作为显示消息的一种;
158 */
159 constexpr char HeartbeatMessage[] = "I'm alive";
160 
161 /////////////////////////////////////////////////////////////////////
162 /// 每一个通信的内容格式
163 /*
164  * struct: CommunicateProtocol
165  * desc:伙伴进程间通信的协议格式;
166  * Tips:
167  *      1.毕竟采用珍贵的内存资源,因此协议体能小尽可能的小;
168  *      2.消息内容只发送一种,那就是与消息类型匹配的那一种;
169  *      3.msgJumpNum 消息存活的跳数由消息发送者指定,由管理者维护;
170  * 一般来说,点对点类型的消息,跳数为1;
171 */
172 typedef struct _Process_Communication_Protocol{
173     unsigned msgLen = 0; //消息长度,根据消息类型显示消息和控制消息仅包含一种,用作取消息;包括自身长度;
174     unsigned char sendType = SendType_Invalid; //消息发送发是否是管理者
175     unsigned char senderId = 0; //消息发送者的ID 0:非法ID; 其他值合法;
176     unsigned char receiverId = 0; //消息接收者的ID 0:非法ID; 其他值合法;
177     unsigned char msgType = MsgType_Invalid; //消息类型;
178     unsigned char msgJumpNum = MessageMaxJumpNumber; //消息的存活跳数;
179     std::string displayMsg; //显示消息内容;
180     unsigned char controlMsg = ControlMsgType_Invalid; //控制消息内容
181 
182     std::shared_ptr<char> protocol();
183     static std::shared_ptr<_Process_Communication_Protocol> structFromProtocol(char* protocol);
184     bool isValid(){return (wholeSize() == msgLen);}
185     unsigned wholeSize();
186 }ProcessCommProtocol;
187 
188 
189 /////////////////////////////////////////////////////////////////////
190 /// 收发的消息队列
191 /// 1. 整个伙伴群成员,分为一个管理者和多个员工。 所有员工的消息都直接汇总到管理着, 员工只接收从管理着发来的消息。
192 /// 2. 进程 “ProcessOne ” 充当管理者, 进程“ProcessTwo” 和 “ProcessThree” 充当员工。
193 /// 3. 收到某伙伴消息,则认为伙伴活着的;超过 n 次未收到某伙伴的消息,则认为已经离线,并始终未 n,不在递增;
194 /// 4. 接收队列里的进程ID都是发送者的ID,接收者默认是自己;
195 /// 5. 发送队列里的进程ID都是接收者的ID,发送者默认是自己;
196 
197 /*
198  * classType:DisplayMessage
199  * desc: 显示消息元组
200  *
201  * get<0>(DisplayMessage):进程ID
202  * get<1>(DisplayMessage):消息内容
203 */
204 typedef std::tuple<unsigned char, std::string> DisplayMessage;
205 
206 /*
207  * classType:ControlMessage
208  * desc: 控制消息元组
209  *
210  * get<0>(ControlMessage):进程ID
211  * get<1>(ControlMessage):消息内容
212 */
213 typedef std::tuple<unsigned char, unsigned char> ControlMessage;
214 
215 extern std::deque<DisplayMessage>   recvDisplayMsg; //收到的伙伴(员工)的消息队列;
216 extern std::deque<ControlMessage>   recvControlMsg;
217 extern std::deque<DisplayMessage>   sendDisplayMsg; //需要发送给伙伴(员工)的消息队列;
218 extern std::deque<ControlMessage>   sendControlMsg;
219 extern std::set<unsigned char>      alivePartner; //“活着的”伙伴进程ID
220 extern std::map<unsigned char, unsigned char> partnerOffline; //伙伴离线计数 <ID, Count>
221 
222 /////////////////////////////////////////////////////////////////////
223 /// 对共享内存的读和写
224 ///
225 /// 共享内存里存放消息的协议为:消息数量+消息1+消息2+...+消息n;
226 /// 消息数量:unsigned short 两字节; 指定为最大值时为无消息内容(当然0也是无消息内容);
227 /// 消息n: ProcessCommProtocol 结构体内容;
228 ///
229 /*
230  * variable: sharedMemoryBuf
231  * desc: 共享内存的地址
232  * tips: 读写操作前,先对 sharedMemoryBuf 赋值; 读写操作后,再对 sharedMemoryBuf 重置;
233 */
234 extern char * sharedMemoryBuf;
235 
236 /*
237  * variable: currentSharedMemorySize
238  * desc: 当前共享内存的大小
239 */
240 extern size_t currentSharedMemorySize;
241 
242 /*
243  * func: updateMessageDeque
244  * desc: 刷新消息队列。 从消息队列中读取;& 维护消息队列; & 把消息写入到队列中;
245  * Tips: 调用一次,就完成一次所有消息的读取,所有消息的写入;
246 */
247 extern void updateMessageDeque();
248 
249 /////////////////////////////////////////////////////////////////////
250 /// 共享内存QSharedMemory
251 static QSharedMemory sharedMemory(ProcessCommunicationSharedMemory);
252 #endif //BASE_COMMON_H

 

.cpp

  1 #include "ProcessCommunicate.h"
  2 
  3 /*
  4  * variable: Id_ProcessName
  5  * desc: 所有伙伴进程的ID编号和进程名称的对应关系映射
  6  * Lable:[Change]
  7 */
  8 std::map<unsigned char, std::string> ID_ProcessName = \
  9 {\
 10     {0, ProcessOne}, {1, ProcessTwo},{2, ProcessThree}
 11 };
 12 
 13 
 14 /////////////////////////////////////////////////////////////////////
 15 /// 对共享内存的读和写
 16 ///
 17 /// 共享内存里存放消息的协议为:消息数量+消息1+消息2+...+消息n;
 18 /// 消息数量:unsigned short 两字节; 指定为最大值时为无消息内容(当然0也是无消息内容);
 19 /// 消息n: ProcessCommProtocol 结构体内容;
 20 ///
 21 
 22 /*
 23  * variable: sharedMemoryBuf
 24  * desc: 共享内存的地址
 25  * tips: 读写操作前,先对 sharedMemoryBuf 赋值; 读写操作后,再对 sharedMemoryBuf 重置;
 26 */
 27 char * sharedMemoryBuf = nullptr;
 28 
 29 
 30 /*
 31  * variable: currentSharedMemorySize
 32  * desc: 当前共享内存的大小
 33 */
 34 size_t currentSharedMemorySize = 0;
 35 
 36 /*
 37  * variable: messageList
 38  * desc: 消息内容项的链表
 39  * tips:
 40  *      读取(read)共享内存时,同时记录下每一个消息内容项到链表里管理起来,
 41  * 以便重新整理(recoginze)和写(write)消息时使用。
 42 */
 43 std::list<std::shared_ptr<ProcessCommProtocol> > messageList;
 44 
 45 /*
 46  * struct: SharedMemoryGuard
 47  * desc: 守护 sharedMemoryBuf 使用后一定会得到重置;
 48 */
 49 struct SharedMemoryBufGuard{
 50     SharedMemoryBufGuard(){
 51         messageList.clear();
 52     }
 53     ~SharedMemoryBufGuard(){
 54         sharedMemoryBuf = nullptr;
 55         messageList.clear();
 56     }
 57 };
 58 
 59 
 60 /*
 61  * func:readMessageFromSharedMemory
 62  * desc:读取消息内容, 一次性全部读取; & 维护伙伴计数(partnerOffline); & 更新“活着的”伙伴(alivePartner);
 63  * tips:
 64  *  1.伙伴进程(员工)只读取来自管理者的消息;
 65  *  2.管理者进程读取所有来自伙伴进程的消息;不读取来自管理者的消息;
 66  * 调用关系: readMessageFromSharedMemory() + reorganizeSharedMemory() + writeMessageToSharedMemory()
 67  *  3. 内部调用
 68 */
 69 void readMessageFromSharedMemory()
 70 {
 71     if (!sharedMemoryBuf){
 72         return ;
 73     }
 74 
 75     unsigned short offset = 0; //计算偏移
 76 
 77     //读取:共享内存协议
 78     unsigned short msgCount = 0; //消息内容项的数量; -1:没有消息内容项;
 79     memcpy((char*)&msgCount, sharedMemoryBuf+offset, sizeof(msgCount));
 80     offset += sizeof(msgCount);
 81 
 82     for (; msgCount != NonMessage && msgCount-- > 0;)//循环处理每一个消息内容项
 83     {
 84 
 85         std::shared_ptr<ProcessCommProtocol> aMessage(\
 86                     ProcessCommProtocol::structFromProtocol(\
 87                         sharedMemoryBuf+offset));
 88 
 89         offset += aMessage->wholeSize();
 90 
 91         if (!aMessage){
 92             continue;
 93         }
 94 
 95         //不管是不是自己需要接收的消息,都需要连接到消息链表中,用于重新整理链表使用;
 96         messageList.push_back(aMessage);
 97 
 98         ///start:在取出具体的消息内容之前,先判断该消息是否是当前进程需要的
 99         //伙伴进程(普通)只接收来自管理者的消息;伙伴间的消息,必须汇总到管理者处,再由管理者派发,包括心跳;
100         if(!Is_Manager && ID_Manager != aMessage->senderId){
101             continue;
102         }
103 
104         //进程自己不接收自己发出的消息
105         if (ID_Owner == aMessage->senderId){
106             continue;
107         }
108 
109         //进程只接收发给自己的消息,不是自己的不收
110         if (ID_Owner != aMessage->receiverId){
111             continue;
112         }
113         ///end:
114 
115         ///start: 如何处理收到的消息?
116         //1. 仅仅需要把收到的消息分类放入消息队列即可
117         if (MsgType_Control == aMessage->msgType && ControlMsgType_Invalid != aMessage->controlMsg){
118             recvControlMsg.push_back(ControlMessage(aMessage->senderId, aMessage->controlMsg));
119         }
120         else if (MsgType_Display == aMessage->msgType){
121             recvDisplayMsg.push_back(DisplayMessage(aMessage->senderId, aMessage->displayMsg));
122         }
123         else{
124             //非法消息
125         }
126         ///end:
127     }
128 
129 }
130 
131 /*
132  * struct: IteratorPlusGuard
133  * desc: 迭代器自动增加守卫
134 */
135 template<typename T>
136 struct IteratorPlusGuard{
137     IteratorPlusGuard(T& t):_t(t){}
138     ~IteratorPlusGuard(){
139         ++_t;
140     }
141     T& _t;
142 };
143 
144 /*
145  * func:reorganizeSharedMemory
146  * desc:重新整理共享内存。原则,尽量减轻伙伴进程的负担。
147  * tips:
148  *  1. 管理者进程,整理消息内容;
149  *  2. 管理者进程,删除不再使用的消息; & 把伙伴进程发送给伙伴进程的消息,重定向为由管理者发送给伙伴进程的消息;
150  *  3. 管理者和伙伴进程,删除发送者是管理者,接收者是自己,且不是广播(心跳)的消息;
151  *  4. 管理者和伙伴进程,维护伙伴计数(partnerOffline); & 更新“活着的”伙伴(alivePartner);
152  *  5. 内部调用
153 */
154 void reorganizeSharedMemory()
155 {
156     if (!sharedMemoryBuf){
157         return ;
158     }
159 
160     //维护每一个伙伴进程是否存活
161     for (auto iterator = partnerOffline.begin(); iterator != partnerOffline.end(); ++iterator){
162         partnerOffline[iterator->first] += 1;//默认每一次加1
163     }
164 
165     //遍历每一个消息内容项
166     auto iterator = messageList.begin();
167     while(iterator != messageList.end()){
168         auto aMessage = *iterator;
169 
170         ///start: 维护伙伴进程是否存活,只要发送者出现一次某伙伴,就代表该伙伴儿活着的
171         partnerOffline[aMessage->senderId] = 0; //0:离线次数为0, 伙伴活着的;
172 
173         if (Is_Manager){
174             //管理者需要把伙伴上报的心跳包打散并分发
175             //但不用分发管理者的心跳包,因为每一种包都是由管理者发出的,已经可以体现出管理者的心跳
176             if (ID_Manager != aMessage->senderId && SendType_Broadcast == aMessage->sendType){
177                 for (auto partner : alivePartner){
178                     if (ID_Manager == partner /*|| partner == aMessage->senderId*/){
179                         //不用再次发给管理者
180                         //需要把心跳发给发出心跳进程自己,表示管理者存在,伙伴进程才能看到心跳
181                         continue;
182                     }
183                     sendControlMsg.push_back(ControlMessage(partner, aMessage->senderId));//打散后分发心跳
184                 }
185             }
186         }
187         else{//非管理者才需要特别的解析伙伴的心跳
188             if (MsgType_Control == aMessage->msgType \
189                     && ControlMsgType_Invalid > aMessage->controlMsg){//表示上报心跳的伙伴进程的ID
190                 partnerOffline[aMessage->controlMsg] = 0; //0:离线次数为0, 伙伴活着的;
191             }
192         }
193         ///end:
194 
195         if (Is_Manager){//管理者进程-删除不合法,或不再需要的消息
196             ///start: 消息的类型不合法,则消息不再继续传递
197             if (MsgType_Invalid == aMessage->msgType){
198                 iterator = messageList.erase(iterator);
199                 continue;
200             }
201             ///end:
202 
203             ///start: 消息的发送类型不合法,则消息不再继续传递
204             if (SendType_Invalid == aMessage->sendType){
205                 iterator = messageList.erase(iterator);
206                 continue;
207             }
208             ///end:
209 
210             ///start: 消息是控制消息,但控制消息的具体内容不合法
211             if (MsgType_Control == aMessage->msgType && ControlMsgType_Invalid == aMessage->controlMsg){
212                 iterator = messageList.erase(iterator);
213                 continue;
214             }
215             ///end:
216 
217             ///start: 消息的接收者为管理者,则消息不再继续传递;
218             if (ID_Manager == aMessage->receiverId){
219                 iterator = messageList.erase(iterator);
220                 continue;
221             }
222             ///end:
223 
224             ///start: 屏蔽伙伴进程自己给自己发消息;
225             if (ID_Manager != aMessage->senderId \
226                     && aMessage->senderId == aMessage->receiverId){
227                 iterator = messageList.erase(iterator);
228                 continue;
229             }
230             ///end:
231 
232             ///start: 维护消息内容项的存活跳数
233             //及时删除无用的消息。例如:某伙伴在未收到消息的时候退出了。
234             //并且需要反向写入到共享内存中,在 writeMessageToSharedMemory 完成
235             if (ID_Manager == aMessage->senderId){
236                 --aMessage->msgJumpNum;
237             }
238             else{
239                 //伙伴发给伙伴的消息,等管理者重定向之后再维护跳数
240             }
241             //如果消息内容项的跳数减少为0,则把该条消息内容项删掉
242             if (0 == aMessage->msgJumpNum){
243                 iterator = messageList.erase(iterator);
244                 continue;
245             }
246             ///end:
247         }//if (Is_Manager)
248 
249         ///start: 管理者和伙伴进程都不再需要由管理者发给自己的消息
250         //及时删除作废的消息, 人人有责
251         if (ID_Manager == aMessage->senderId && ID_Owner == aMessage->receiverId)
252         {
253             iterator = messageList.erase(iterator);
254             continue;
255         }
256         ///end:
257 
258         IteratorPlusGuard<decltype (messageList.begin()) > itGuard(iterator);
259 
260         if (Is_Manager){//管理者改写消息,例如:伙伴发给伙伴的;
261 
262             ///start: 把伙伴进程发送给伙伴进程的消息,重定向为由管理者发送给伙伴进程的消息
263             if (ID_Manager != aMessage->senderId && ID_Manager != aMessage->receiverId){
264                 aMessage->sendType = SendType_PointToPoint;
265                 aMessage->senderId = ID_Manager;
266                 aMessage->msgJumpNum = 1;
267                 continue;
268             }
269             ///end:
270         }
271 
272     }//while(iterator != messageList.end())
273 
274     //维护活着的伙伴儿
275     alivePartner.clear();
276     for (auto iterator = partnerOffline.begin(); iterator != partnerOffline.end(); ++iterator){
277         if (partnerOffline[iterator->first] < JudgePartnerOfflineMaxCount){
278             alivePartner.insert(iterator->first);
279         }
280     }
281 }
282 
283 /*
284  * func:writeMessageToSharedMemory
285  * desc:写消息内容, 一次性全部写入;
286  * tips:
287  *  1. 伙伴进程(员工)追加写入;管理者追加写入;
288  *  2. 内部调用
289  *
290 */
291 void writeMessageToSharedMemory()
292 {
293     if (!sharedMemoryBuf){
294         return ;
295     }
296 
297     //普通消息;
298     for (auto msg : sendDisplayMsg){
299         std::shared_ptr<ProcessCommProtocol> aMessage(new ProcessCommProtocol);
300         //aMessage->msgLen;
301         aMessage->displayMsg = std::get<1>(msg);
302         aMessage->sendType = (HeartbeatMessage == aMessage->displayMsg)?\
303                     SendType_Broadcast:SendType_PointToPoint;
304         aMessage->senderId = ID_Owner;
305         aMessage->receiverId = std::get<0>(msg);
306         aMessage->msgType = MsgType_Display;
307         aMessage->msgJumpNum = 1; //点对点跳数1就够了。广播会在管理者收到广播后转换成点对点散出去。
308 
309         //aMessage->displayMsg;
310         //aMessage->controlMsg 对于显示消息,控制消息默认非法
311 
312         aMessage->msgLen = aMessage->wholeSize();
313         messageList.push_back(aMessage);
314     }
315     sendDisplayMsg.clear();
316 
317     //控制消息:
318     for (auto msg : sendControlMsg){
319         std::shared_ptr<ProcessCommProtocol> aMessage(new ProcessCommProtocol);
320         //aMessage->msgLen;
321         aMessage->sendType = SendType_PointToPoint;
322         aMessage->senderId = ID_Owner;
323         aMessage->receiverId = std::get<0>(msg);
324         aMessage->msgType = MsgType_Control;
325         aMessage->msgJumpNum = 1; //点对点跳数1就够了。
326 
327         //aMessage->displayMsg; 对于控制消息,显示消息默认为空
328         aMessage->controlMsg = std::get<1>(msg);
329         aMessage->msgLen = aMessage->wholeSize();
330         messageList.push_back(aMessage);
331     }
332     sendControlMsg.clear();
333 
334     ///start: 写入共享内存之前,先把共享内存重置
335     memset(sharedMemoryBuf, 0, currentSharedMemorySize);
336     ///end:
337 
338     ///start: 写入共享内存
339     size_t offset = 0;
340     unsigned short msgCount = messageList.size();
341     memcpy(sharedMemoryBuf + offset, (char*)&msgCount, sizeof(msgCount));
342     offset += sizeof(msgCount);
343 
344     for (auto aMessage : messageList){
345         auto protocol = aMessage->protocol();
346         if (!protocol){
347             continue;
348         }
349         memcpy(sharedMemoryBuf + offset, protocol.get(), aMessage->wholeSize());
350         offset += aMessage->wholeSize();
351     }
352     ///end:
353 }
354 
355 
356 /////////////////////////////////////////////////////////////////////
357 /// 收发的消息队列
358 /// 1. 整个伙伴群成员,分为一个管理者和多个员工。 所有员工的消息都直接汇总到管理着, 员工只接收从管理着发来的消息。
359 /// 2. 进程 “ProcessOne ” 充当管理者, 进程“ProcessTwo” 和 “ProcessThree” 充当员工。
360 /// 3. 收到某伙伴消息,则认为伙伴活着的;超过 n 次未收到某伙伴的消息,则认为已经离线,并始终未 n,不在递增;
361 /// 4. 接收队列里的进程ID(消息接收者)都是自己;
362 /// 5. 发送队列里的进程ID(消息接收者)都是目标;
363 std::deque<DisplayMessage>   recvDisplayMsg; //收到的伙伴(员工)的消息队列;
364 std::deque<ControlMessage>   recvControlMsg;
365 std::deque<DisplayMessage>   sendDisplayMsg; //需要发送给伙伴(员工)的消息队列;
366 std::deque<ControlMessage>   sendControlMsg;
367 std::set<unsigned char>      alivePartner; //“活着的”伙伴进程ID
368 std::map<unsigned char, unsigned char> partnerOffline; //伙伴离线计数 <ID, Count>
369 
370 
371 /////////////////////////////////////////////////////////////////////
372 /// 每一个通信的内容格式
373 std::shared_ptr<char> _Process_Communication_Protocol::protocol(){
374     if (!isValid()){
375         return nullptr;
376     }
377 
378     std::shared_ptr<char> buf(new char[wholeSize()+1]);
379     unsigned offset = 0;
380 
381     ///这里不要用 memset() 对 buf 做初始化,0值对于某个协议成员来说是有意义的;
382     ///整个buf都会满满的填充上协议内容,没有多余的;
383 
384     //消息长度
385     memcpy(buf.get() + offset, (char*)&msgLen, sizeof(msgLen));
386     offset += sizeof(msgLen);
387 
388     //消息发送发是否是管理者
389     memcpy(buf.get() + offset, (char*)&sendType, sizeof(sendType));
390     offset += sizeof(sendType);
391 
392     //消息发送者的ID
393     memcpy(buf.get() + offset, (char*)&senderId, sizeof(senderId));
394     offset += sizeof(senderId);
395 
396     //消息接收者的ID
397     memcpy(buf.get() + offset, (char*)&receiverId, sizeof(receiverId));
398     offset += sizeof(receiverId);
399 
400     //消息类型
401     memcpy(buf.get() + offset, (char*)&msgType, sizeof(msgType));
402     offset += sizeof(msgType);
403 
404     //消息的存活跳数
405     memcpy(buf.get() + offset, (char*)&msgJumpNum, sizeof(msgJumpNum));
406     offset += sizeof(msgJumpNum);
407 
408     //消息内容
409     if (MsgType_Display == msgType){
410         memcpy(buf.get() + offset, displayMsg.c_str(), displayMsg.length());
411         offset += displayMsg.length();
412     }
413     else if (MsgType_Control == msgType){
414         memcpy(buf.get() + offset, (char*)&controlMsg, sizeof(controlMsg));
415         offset += sizeof(controlMsg);
416     }
417     else{
418         return nullptr;
419     }
420     (void)offset;
421     return buf;
422 }
423 
424 std::shared_ptr<_Process_Communication_Protocol> _Process_Communication_Protocol::structFromProtocol(char *protocol){
425     if (!protocol){
426         return nullptr;
427     }
428     std::shared_ptr<_Process_Communication_Protocol> aMessage(new _Process_Communication_Protocol);
429     unsigned offset = 0;
430     memcpy((char*)&aMessage->msgLen, protocol+ offset, sizeof(aMessage->msgLen));
431     offset += sizeof(aMessage->msgLen);
432 
433     memcpy((char*)&aMessage->sendType,protocol+ offset, sizeof(aMessage->sendType));
434     offset += sizeof(aMessage->sendType);
435 
436     memcpy((char*)&aMessage->senderId,protocol+ offset, sizeof(aMessage->senderId));
437     offset += sizeof(aMessage->senderId);
438 
439     memcpy((char*)&aMessage->receiverId,protocol+ offset, sizeof(aMessage->receiverId));
440     offset += sizeof(aMessage->receiverId);
441 
442     memcpy((char*)&aMessage->msgType,protocol+ offset, sizeof(aMessage->msgType));
443     offset += sizeof(aMessage->msgType);
444 
445     memcpy((char*)&aMessage->msgJumpNum,protocol+ offset, sizeof(aMessage->msgJumpNum));
446     offset += sizeof(aMessage->msgJumpNum);
447 
448     if (MsgType_Display == aMessage->msgType){
449 
450         unsigned char fixedLen = 0;
451         fixedLen += sizeof(aMessage->msgLen);
452         fixedLen += sizeof(aMessage->sendType);
453         fixedLen += sizeof(aMessage->senderId);
454         fixedLen += sizeof(aMessage->receiverId);
455         fixedLen += sizeof(aMessage->msgType);
456         fixedLen += sizeof(aMessage->msgJumpNum);
457 
458         std::shared_ptr<char> msg(new char[(aMessage->msgLen - fixedLen) +1]);
459         memset(msg.get(), 0, (aMessage->msgLen - fixedLen) +1);
460         memcpy(msg.get(),protocol+ offset, aMessage->msgLen - fixedLen);
461         aMessage->displayMsg = std::string(msg.get());
462         offset += (aMessage->msgLen - fixedLen);
463     }
464     else if (MsgType_Control == aMessage->msgType){
465         memcpy((char*)&aMessage->controlMsg,protocol+ offset, sizeof(aMessage->controlMsg));
466         offset += sizeof(aMessage->controlMsg);
467     }
468     else{
469         return nullptr;
470     }
471     (void)offset;
472     return aMessage;
473 }
474 
475 unsigned _Process_Communication_Protocol::wholeSize(){
476     unsigned size = 0;
477     if (MsgType_Display == msgType){
478         size = displayMsg.length();
479     }
480     else if (MsgType_Control == msgType){
481         size = sizeof(controlMsg);
482     }
483     else{
484         return 0;
485     }
486 
487     return (size\
488             + sizeof(msgLen)\
489             + sizeof(sendType)\
490             + sizeof(senderId)\
491             + sizeof(receiverId)\
492             + sizeof(msgType)\
493             + sizeof(msgJumpNum));
494 }
495 
496 
497 /////////////////////////////////////////////////////////////////////
498 /// 共享内存QSharedMemory
499 /*
500  * struct: SharedMemoryGuard
501  * desc: 守护 m_sharedMemory 使用前一定 lock(), 并赋值给 sharedMemoryBuf;使用后一定 unlock;
502 */
503 struct SharedMemoryGuard{
504     SharedMemoryGuard(){
505         if (Is_Manager){
506             if (!sharedMemory.isAttached()){
507                 if(!sharedMemory.create(1024)){
508                     //qDebug("create shared memory segment failed.");
509                     sharedMemory.attach();
510                 }
511                 else{
512                     //qDebug("create shared memory segment successed.");
513                 }
514             }
515             else{
516                 //qDebug("already create shared memory segment.");
517             }
518         }
519         else{
520             if (sharedMemory.attach()){
521                 //qDebug("shared memory attach successed.");
522             }
523             else{
524                 //qDebug("shared memory attach failed.");
525             }
526         }
527 
528         if (sharedMemory.isAttached()){
529             sharedMemory.lock();
530             sharedMemoryBuf = (char*)sharedMemory.data();
531             currentSharedMemorySize = sharedMemory.size();
532         }
533         else {
534             sharedMemoryBuf = nullptr;
535             currentSharedMemorySize = 0;
536         }
537     }
538     ~SharedMemoryGuard(){
539         if (sharedMemory.isAttached()){
540             sharedMemory.unlock();
541         }
542     }
543 };
544 
545 /*
546  * func: updateMessageDeque
547  * desc: 刷新消息队列。 从消息队列中读取;& 维护消息队列; & 把消息写入到队列中;
548  * Tips: 调用一次,就完成一次所有消息的读取,所有消息的写入;
549 */
550 void updateMessageDeque()
551 {
552     SharedMemoryGuard guard;
553     SharedMemoryBufGuard bufGuard;
554     readMessageFromSharedMemory();
555     reorganizeSharedMemory();
556     writeMessageToSharedMemory();
557 }

 

原文链接: https://www.cnblogs.com/azbane/p/16282488.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

    采用QSharedMemory实现多进程间的通信 Linux | window

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/398236

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年4月12日 上午9:34
下一篇 2023年4月12日 上午9:35

相关推荐