採用QSharedMemory實現多程序間的通訊 Linux | window
阿新 • • 發佈:2022-05-17
直接上程式碼:簡單的很
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 }