1. 程式人生 > 其它 >採用QSharedMemory實現多程序間的通訊 Linux | window

採用QSharedMemory實現多程序間的通訊 Linux | window

直接上程式碼:簡單的很

  1 /////////////////////////////////////////////////////////////////////
  2 /// file: ProcessCommunicate.h
  3 /// Description:本文采用<QSharedMemory>實現同一臺電腦上多個程序間的通訊。使用極其的簡單,你只需要呼叫一個無引數函式就能完成。
  4 ///
  5 /// Copyright: 成都中科希奧科技有限公司 Official Website:https://siotech.net/
  6 /// Author: 張洪銘-小熊博(zhm-xxbs) Email:[email protected]
  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 }