C++實現進程端口網絡數據接收系統設計示例程序

一、問題描述

最近做了一道簡單的系統設計題,大概描述如下:
1.一個進程可以綁定多個端口,用于監聽接收網絡中的數據,但是一個端口只能被一個進程占用
2.1 <= pid <= 65535, 1 <= port <= 100000, 1 <= topNum <= 5, 0 <= packetLen < 1000
類接口函數聲明如下,要求實現其中每個函數,滿足程序要求。

class NetWorkRecvSystem
{
public:NetWorkRecvSystem();// 將某個端口和進程綁定bool BindPort(int pid, int port);// 解除端口port的綁定,如果port未被當前系統中的進程占用,則返回falsebool UnBindPort(int port);// 在端口port上接收到字節數為packetLen長度的網絡數據// 如果當前端口已被解綁或未被進程占用,則直接返回0// 否則該端口對應的進程的接收數據總長度累加上當前的dataLen,返回最后的總長度int RecvNetData(int port, int dataLen);// 統計總接收數據長度排名前topNum的進程列表// 按照如下規則進行排序輸出:// 1.先按照進程的總數據接收長度從大到小降序排序// 2.如果兩個進程的數據接收總長度相等,則按照進程pid從小到大升序// 最后返回前topNum個進程的列表// 注意:數據長度為0的進程不輸出,如果所有進程都沒有接收到數據,則返回空列表{}std::vector<int> statTopNum(int topNum);
};

舉例1:
輸入:
NetWorkRecvSystem sys; // 創建一個系統變量
sys.BindPort(12345, 80);
sys.BindPort(67890, 3306);
sys.BindPort(12345, 8080);
sys.statTopNum(2); // 由于當前進程只做端口綁定,還未接收到數據,所以返回空列表 []
sys.RecvNetData(80, 100); // 端口80上接收到100字節的網絡數據,此時進程12345的總數據接收長度為100
sys.RecvNetData(3306, 300); // 端口3306上接收到300字節的網絡數據,此時進程67890的總數據接收長度為300
sys.statTopNum(1); // 由于此時進程67890的總長度為300,大于進程12345的總數據接收長度100,所以返回[67890]
sys.RecvNetData(80,200); // 123456 -> 300, 67890 -> 300
sys.BindPort(34567, 3306); // false
sys.BindPort(34567, 21);
sys.RecvNetData(21,400); // 34567 -> 400,此時123456 -> 300, 67890 -> 300
sys.statTopNum(5); // [34567, 123456, 67890]
sys.UnBindPort(21);
sys.statTopNum(1); // [34567]

系統設計

做系統設計這類題目,首選要讀懂題意,其次再選擇合適的數據結構用于保存數據,我首先想到用一個std::map<int, ProcessItem>的接口來保存每個進程的網絡端口和數據包接收信息,其中ProcessItem結構如下:

struct ProcessItem
{int processId = -1;		// 進程的pid,唯一標識std::set<int> ports;	// 進程所占用的端口集合,一個進程可占用多個不同的端口int packetLen = 0;		// 進程所有端口接收到的總報文字節數
};

后面實際寫代碼過層中發現std::map是個紅黑樹結構,不太好排序,而且會有些數據冗余;只用std::vector<ProcessItem> procItemVec;數組就能滿足要求,而且結合C++ STL algorithmstd::vector排序很方便。

還有一個要注意的點,對std::vector循環遍歷時,如果要erase刪除某個元素,要注意迭代器失效的問題,這個可以參考我之前的一篇博客:C++ vector迭代器失效

C++代碼實現:

NetWorkSystem.h頭文件

#include <vector>
#include <set>using std::vector;
using std::set;struct ProcessItem
{int processId = -1;		// 進程的pid,唯一標識std::set<int> ports;	// 進程所占用的端口集合,一個進程可占用多個不同的端口int packetLen = 0;		// 進程所有端口接收到的總報文字節數
};class NetWorkSystem
{
public:NetWorkSystem();~NetWorkSystem();// 將某個端口和進程綁定bool BindPort(int pid, int port);// 解除端口port的綁定,如果port未被當前系統中的進程占用,則返回falsebool UnBindPort(int port);// 在端口port上接收到字節數為packetLen長度的網絡數據// 如果當前端口已被解綁或未被進程占用,則直接返回0// 否則該端口對應的進程的接收數據總長度累加上當前的dataLen,返回最后的總長度int RecvNetPacketData(int port, int packetLen);// 統計總接收數據長度排名前topNum的進程列表// 按照如下規則進行排序輸出:// 1.先按照進程的總數據接收長度從大到小降序排序// 2.如果兩個進程的數據接收總長度相等,則按照進程pid從小到大升序// 最后返回前topNum個進程的列表// 注意:數據長度為0的進程不輸出,如果所有進程都沒有接收到數據,則返回空列表{}std::vector<int> statTopNum(int topNum);private:std::vector<ProcessItem> procItemVec;	// 數據,用來保存進程和端口映射的數組
};

NetWorkSystem.cpp實現文件:

#include "NetWorkSystem.h"
#include <algorithm>NetWorkSystem::NetWorkSystem()
{
}NetWorkSystem::~NetWorkSystem()
{
}bool NetWorkSystem::BindPort(int pid, int port)
{if (pid <= 0 || port <= 0) {return false;}// 如果端口port已被其他進程占用,則不處理,直接返回falsefor (auto procIter : procItemVec) {if (procIter.ports.count(port) != 0) {return false;}}auto iter = std::find_if(procItemVec.begin(), procItemVec.end(), [pid](const ProcessItem item) {return pid == item.processId;});// 如果之前有進程,則將其插入到對應進程的ports集合中(集合可以去重)if (iter != procItemVec.end()) {iter->ports.insert(port);} else {// 之前沒有該進程,則新建一項,初始化進程信息,并放入到數組中ProcessItem procItem;procItem.processId = pid;std::set<int> portSet = { port };procItem.ports = portSet;procItem.packetLen = 0;procItemVec.push_back(procItem);}return true;
}bool NetWorkSystem::UnBindPort(int port)
{if (port <= 0) {return false;}// 如果端口port被其他進程占用,則從對應進程的端口集合中解綁,直接返回truefor (auto procIter : procItemVec) {auto portIter = procIter.ports.find(port);// 找到對應的端口portif (portIter != procIter.ports.end()) {// 將該端口中對應進程的端口集合中移除procIter.ports.erase(port);return true;}}// 如果沒找到該端口,則返回falsereturn false;
}int NetWorkSystem::RecvNetPacketData(int port, int packetLen)
{if (port <= 0 || packetLen <= 0) {return 0;}for (auto procIter = procItemVec.begin(); procIter != procItemVec.end(); procIter++) {// 找到對應的端口if (procIter->ports.count(port) != 0) {procIter->packetLen += packetLen;return procIter->packetLen;}}return 0;
}// 統計接收網絡數據包總長度前topNum的進程列表
std::vector<int> NetWorkSystem::statTopNum(int topNum)
{std::vector<int> pidList;// 1. 先緩存進程信息列表(對緩存數據進行處理,防止原始數據procItemVec被弄臟)auto procItemVecTemp = procItemVec;// 2. 移除那些網絡數據包為0的進程項for (auto iter = procItemVecTemp.begin(); iter != procItemVecTemp.end();) {if (iter->packetLen == 0) {iter = procItemVecTemp.erase(iter);	// 注意:vector在循環時做erase操作很容易導致迭代器失效問題} else {iter++;}}// 3. 如果procItemVecTemp長度為0,即所有進程都沒有接收到數據包,則返回空列表if (procItemVecTemp.size() == 0) {return std::vector<int>();}// 4. 對第3步處理后的進程信息數據按照規則進行排序// 規則1: 先根據進程的packetLen長度從大到小降序// 規則2: 如果兩個進程項的packetLen相等,則按照進程processId從小到大升序std::sort(procItemVecTemp.begin(), procItemVecTemp.end(), [](const ProcessItem item1, const ProcessItem item2) {if (item1.packetLen == item2.packetLen) {return item1.processId < item2.processId;}return item1.packetLen > item2.packetLen;});// 5. 只輸出procItemVecTemp中排名topNum的進程pid列表int processCnt = topNum;for (auto procIter = procItemVecTemp.begin(); procIter != procItemVecTemp.end() && processCnt > 0; procIter++) {pidList.push_back(procIter->processId);if (processCnt-- <= 0) {break;}}return pidList;
}

main.cpp

#include <iostream>
#include "NetWorkSystem.h"void PrintVector(std::vector<int> nums)
{std::cout << "[";for (auto iter = nums.begin(); iter != nums.end(); iter++) {if (iter != nums.end() - 1) {std::cout << *iter << ","} else {std::cout << *iter;}}std::cout << "]" << std::endl;
}void NetWorkSystem_test_001()
{std::vector<int> pidListResult = {};NetWorkSystem sys;	// 創建一個系統變量sys.BindPort(12345, 80);sys.BindPort(67890, 3306);sys.BindPort(12345, 8080);std::cout << "--------------- 111 start ----------------------------" << std::endl;pidListResult = sys.statTopNum(2);		// 由于當前進程只做端口綁定,還未接收到數據,所以返回空列表 []PrintVector(pidListResult);std::cout << "--------------- 111 end ----------------------------" << std::endl;sys.RecvNetPacketData(80, 100);	// 端口80上接收到100字節的網絡數據,此時進程12345的總數據接收長度為100sys.RecvNetPacketData(3306, 300); // 端口3306上接收到300字節的網絡數據,此時進程67890的總數據接收長度為300std::cout << "--------------- 222 start ----------------------------" << std::endl;pidListResult = sys.statTopNum(1);	// 由于此時進程67890的總長度為300,大于進程12345的總數據接收長度100,所以返回[67890]PrintVector(pidListResult);std::cout << "--------------- 222 end ----------------------------" << std::endl;sys.RecvNetPacketData(80, 200); // 123456 -> 300, 67890 -> 300sys.BindPort(34567, 3306); // falsesys.BindPort(34567, 21);sys.RecvNetPacketData(21, 400); // 34567 -> 400,此時123456 -> 300, 67890 -> 300std::cout << "--------------- 333 start ----------------------------" << std::endl;pidListResult = sys.statTopNum(5);  // [34567, 123456, 67890]PrintVector(pidListResult);std::cout << "--------------- 333 end ----------------------------" << std::endl;sys.UnBindPort(21);std::cout << "--------------- 444 start ----------------------------" << std::endl;pidListResult = sys.statTopNum(1); // [34567]PrintVector(pidListResult);std::cout << "--------------- 444 end ----------------------------" << std::endl;
}int main(int argc, char* argv[])
{NetWorkSystem_test_001();
}

代碼運行結果如下圖所示:
代碼運行結果

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/208958.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/208958.shtml
英文地址,請注明出處:http://en.pswp.cn/news/208958.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

ros2/ros安裝ros-dep||rosdep init錯誤

第一個錯誤的做法&#xff1a; sudo apt-get install python3-pip sudo pip3 install 6-rosdep sudo 6-rosdep 如果使用上述代碼將會摧毀整個系統&#xff0c;不重裝系統反正我是搞不定啊&#xff0c;因為我不知道那個寫軟件的人到底做了什么。因為這個我安裝的版本是humble&…

AlexNet 閱讀筆記

“ImageNet Classification with Deep Convolutional Neural Networks” (Krizhevsky 等, 2012, p. 1) 使用深度卷積神經網絡進行 ImageNet 分類 3公式&#xff0c;26個引用&#xff0c;4張圖片&#xff0c;2個簡單表格 Abstract 我們訓練了一個大型深度卷積神經網絡&#…

Leetcode刷題詳解——環繞字符串中唯一的子字符串

1. 題目鏈接&#xff1a;467. 環繞字符串中唯一的子字符串 2. 題目描述&#xff1a; 定義字符串 base 為一個 "abcdefghijklmnopqrstuvwxyz" 無限環繞的字符串&#xff0c;所以 base 看起來是這樣的&#xff1a; "...zabcdefghijklmnopqrstuvwxyzabcdefghijklm…

卷積之后通道數為什么變了

通道數增多與卷積之后得到的圖像特征數量有關 卷積層的作用本來就是把輸入中的特征分離出來變成新的 feature map&#xff0c;每一個輸出通道就是一個卷積操作提取出來的一種特征。在此過程中ReLU激活起到過濾的作用&#xff0c;把負相關的特征點去掉&#xff0c;把正相關的留…

C++:vector增刪查改模擬實現

C:vector增刪查改模擬實現 前言一、迭代器1.1 非const迭代器&#xff1a;begin()、end()1.2 const迭代器&#xff1a;begin()、end() 二、構造函數、拷貝構造函數、賦值重載、析構函數模擬實現2.1 構造函數2.1.1 無參構造2.1.2 迭代器區間構造2.1.3 n個值構造 2.2 拷貝構造2.3 …

vue路由導航守衛(全局守衛、路由獨享守衛、組件內守衛)

目錄 一、什么是Vue路由導航守衛&#xff1f; 二、全局守衛 1、beforeEach 下面是一個beforeEach的示例代碼&#xff1a; 2、beforeResolve 下面是一個beforeResolve的示例代碼&#xff1a; 3、afterEach 下面是一個afterEach的示例代碼&#xff1a; 三、路由獨享守衛…

Shell - 學習筆記 - 1.14 - 如何編寫自己的Shell配置文件(配置腳本)?

第1章 Shell基礎(開胃菜) 14 - 如何編寫自己的Shell配置文件(配置腳本)? 學習了《Shell配置文件的加載》一節,讀者應該知道 Shell 在登錄和非登錄時都會加載哪些配置文件了。對于普通用戶來說,也許 ~/.bashrc 才是最重要的文件,因為不管是否登錄都會加載該文件。 我們…

【數據處理】NumPy數組的合并操作,如何將numpy數組進行合并?

&#xff0c;NumPy中的合并操作是指將兩個或多個數組合并成一個數組的操作。這種操作可以通過不同的函數來實現。 一、橫向合并&#xff08;水平合并&#xff09; 橫向合并是指將兩個具有相同行數的數組按列方向合并成一個數組的操作。在NumPy中&#xff0c;可以使用hstack()…

044:vue中引用json數據的方法

第044個 查看專欄目錄: VUE ------ element UI 專欄目標 在vue和element UI聯合技術棧的操控下&#xff0c;本專欄提供行之有效的源代碼示例和信息點介紹&#xff0c;做到靈活運用。 &#xff08;1&#xff09;提供vue2的一些基本操作&#xff1a;安裝、引用&#xff0c;模板使…

多相Buck的工作原理

什么是多相Buck電源&#xff1f; 多相電源控制器是一種通過同時控制多個電源相位的設備&#xff0c;以提供穩定的電力供應。相位是指電源中的電流和電壓波形。多相控制器的設計旨在最大程度地減小電力轉換系統的紋波&#xff0c;并提高整體能效。它通常包含一系列的功率級聯&a…

我的創作紀念日1024天紀念

機緣 經歷的1024天&#xff0c;突然有一種驚奇&#xff0c;日子一天天過&#xff0c;有種恍惚的感覺 收獲 從最開始的隨筆&#xff0c;慢慢向著筆記總結轉變&#xff0c;不經意間積累了好多 憧憬 雖不知最終會怎樣發展&#xff0c;但堅持與向前是一定的&#xff0c;未來一…

結構化布線系統

滿足下列需求&#xff1a; 1.標準化&#xff1a;國際、國家標準。 2.實用性&#xff1a;針對實際應用的需要和特點來建設系統。 3.先進性&#xff1a;采用國際最新技術。5-10年內技術不落后。 4.開放性&#xff1a;整個系統的開放性。 5.結構化、層次化&#xff1a;易于管理和維…

Matplotlib數據可視化

繪圖基礎語法 &#xff11; 創建畫布并且創建子圖 首先創建一個空白的畫布&#xff0c;并且可以將畫布分為幾個部分&#xff0c;這樣就可以在同一附圖上繪制多個圖像。 plt.figure 創建一個空白畫布&#xff0c;可以指定畫布大小、像素 figure.add_subplot 創建并且選中子…

docker鏡像、容器管理與遷移

鏡像管理 搜索鏡像&#xff1a; 這種方法只能用于官方鏡像庫 搜索基于 centos 操作系統的鏡像 # docker search centos 按星級搜索鏡像&#xff1a; 查找 star 數至少為 100 的鏡像&#xff0c;默認不加 s 選項找出所有相關 ubuntu 鏡像&#xff1a; …

【web安全】文件讀取與下載漏洞

前言 菜某整理僅供學習&#xff0c;有誤請賜教。 概念 個人理解&#xff1a;就是我們下載一個文件會傳入一個參數&#xff0c;但是我們可以修改參數&#xff0c;讓他下載其他的文件。因為是下載文件&#xff0c;所以我們可以看到文件里面的源碼&#xff0c;內容。 文件讀取…

Python嗅探和解析網絡數據包

網絡工具解釋 Scapy是Python2和Python3都支持的庫。 它用于與網絡上的數據包進行交互。 它具有多種功能&#xff0c;通過這些功能我們可以輕松偽造和操縱數據包。 通過 scapy 模塊&#xff0c;我們可以創建不同的網絡工具&#xff0c;如 ARP Spoofer、網絡掃描儀、數據包轉儲器…

swiftUi——顏色

在SwiftUI中&#xff0c;您可以使用Color結構來表示顏色。Color可以直接使用預定義的顏色&#xff0c;例如.red、.blue、.green等&#xff0c;也可以使用自定義的RGB值、十六進制顏色代碼或者系統提供的顏色。 1. 預定義顏色 Text("預定義顏色").foregroundColor(.…

Swing程序設計(9)復選框,下拉框

文章目錄 前言一、復選框二、下拉框總結 前言 該篇文章簡單介紹了Java中Swing組件里的復選框組件、列表框組件、下拉框組件&#xff0c;這些在系統中都是常用的組件。 一、復選框 復選框&#xff08;JCheckBox&#xff09;在Swing組件中的使用也非常廣泛&#xff0c;一個方形方…

Albumentations(Augmentation Transformations)

Albumentations&#xff08;Augmentation Transformations&#xff09; Albumentations&#xff08;Augmentation Transformations&#xff09;是一個用于圖像數據增強&#xff08;數據增廣&#xff09;的Python包。它提供了豐富的圖像增強技術&#xff0c;用于訓練機器學習模…

hadoop安裝與配置-shell腳本一鍵安裝配置(集群版)

文章目錄 前言一、安裝準備1. 搭建集群 二、使用shell腳本一鍵安裝1. 復制腳本2. 增加執行權限3. 分發腳本4. 執行腳本5. 加載用戶環境變量 三、啟動與停止1. 啟動/停止hadoop集群(1) 復制hadoop集群啟動腳本(2) 增加執行權限(3) 啟動hadoop集群(4) 停止hadoop集群(5) 重啟hado…