Linux網絡編程【基于UDP網絡通信的字典翻譯服務】

1. 基本框架:

前面我們已近完成了,基于UDP協議的網絡通信,但是我們服務器接收到來自客戶端的信息即字符串時只是進行了簡單的發送會客戶端和在日志中回顯打印,并沒有實際的業務服務。那么接下來,我們就設計一個字典翻譯的模塊,然后讓網絡通信模塊字典翻譯模塊進行解耦。也就是說,網絡模塊就負責進行通信即可,處理來自客戶端的數據的任務交給字典翻譯模塊!這樣兩個模塊之間就沒有強耦合了!!

2. 走通基本邏輯:

很簡單,我們在服務器內部定義一個包裝器類型func_t,這個包裝器可以接受所有可調用對象,我們在C++11當中就已經說過了。因為我們現在把客戶端發送的字符傳當做英文單詞,我們希望給用戶返回一個漢語。所以我們包裝器的返回值和參數都為string。

這里簡單設計一下字典模塊細節先不填。

?下面是我找的字典配置文件,放在最后了。

下面就是我們模塊和模塊之間進行解耦的原理了,本質就是用函數來進行回調,類用來封裝模塊,完成任務!!?

下面來debug一下,看看邏輯能不能走通~~

OK啊,也是沒有問題的 。那接下來就可以放心的完成字典翻譯模塊啦~~

2. 完善翻譯邏輯【dict.hpp】

#pragma once#include <iostream>
#include <string>
#include <unordered_map>
#include "log.hpp"using namespace log_module;const std::string default_dict = "./dict.txt";
const std::string sep = ": "; // 分隔符class dict
{
public:dict(const std::string &path = default_dict) : _path(path){}// 加載配置文件bool down_load(){// 打開配置文件std::ifstream in(_path);if (!in.is_open()){LOG(log_level::Error) << "打開字典配置文件失敗!" << " 路徑" << _path;return false;}// 讀取配置文件std::string line;while (std::getline(in, line)){// apple: 蘋果 [[9]]auto pos = line.find(sep);if (pos == std::string::npos) // 沒有找到分隔符{LOG(log_level::Warning) << "加載" << line << "錯誤!" << " 跳過";continue;}std::string English = line.substr(0, pos);std::string Chinese = line.substr(pos + sep.size());if (English.empty() || Chinese.empty()){LOG(log_level::Warning) << "加載數據" << line << "無效!" << " 跳過";continue;}_map.insert(std::make_pair(English, Chinese));LOG(log_level::Debug) << "加載" << line << "成功";}in.close();return true;}// translatestd::string translate(const std::string &dict){// LOG(log_level::Debug) << "走到了翻譯邏輯";auto tmp = _map.find(dict);if (tmp == _map.end()){return "None";}return tmp->second;}~dict(){}private:std::string _path;                                 // 配置文件的路徑std::unordered_map<std::string, std::string> _map; // 從配置文件加載的字典
};

測試成功!!?

3. 封裝InetAddr

現在,如果我還有一個需求,就是我們想在服務端翻譯的時候獲取是哪個IP地址,哪個端口號像我們發送的請求。其實,我們在服務端從網絡上獲取客戶端信息時就拿到了該數據只是沒有做處理和記錄下來而已

接下來,我們就來封裝一個InetAddr類來對這些數據做處理,并且我們把處理之后的結果要傳給translate函數,一旦翻譯一條數據后我們就用日志回顯出來。

InetAddr

class InetAddr
{
public:InetAddr(const sockaddr_in &peer) : _peer(peer){_port = ntohs(_port);            // 端口號_ip = inet_ntoa(_peer.sin_addr); // IP地址->點分十進制}uint16_t port(){return _port;}std::string IP(){return _ip;}~InetAddr(){}private:struct sockaddr_in _peer;uint16_t _port;std::string _ip;
};

?具體的回調邏輯:

測試結果我們也看到了本地回環的IP和隨機綁定的端口號。?

至此,我們基于字典翻譯服務的UDP網絡通信就簡單設計完成了。

4. 源碼

server.cc

#include <iostream>
#include "server.hpp"
#include <memory>
#include "dict.hpp"int main(int argc, char *argv[])
{if (argc != 2){std::cout << "Usage: ./server port" << std::endl;return 1;}using_screen_strategy();// std::string ip = argv[1];uint16_t port = std::stoi(argv[1]);dict d;d.down_load();std::unique_ptr<server> p1 = std::make_unique<server>(port,[&d](const std::string& english,InetAddr& client){return d.translate(english,client);});p1->init();p1->start();return 0;
}

server.hpp

#pragma once#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <string>
#include <string.h>
#include <netinet/in.h>
#include "log.hpp"
#include <arpa/inet.h>
#include <functional>
#include "inet_addr.hpp"using namespace log_module;const int default_sockfd = -1;using func_t = std::function<std::string(const std::string &, InetAddr &)>;class server
{
public:server(uint16_t port, func_t func): _sockfd(default_sockfd),_func(func),//   _ip(ip),_port(port),_isrunning(false){}~server(){}// 服務端初始化和啟動void init(){// 創建套接字->打開網絡文件_sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd < 0){LOG(log_level::Fatal) << "socket 創建失敗!";exit(1);}LOG(log_level::Info) << "socket 創建成功!" << _sockfd;// 填充sockaddr_in結構體struct sockaddr_in local;// 數據清空bzero(&local, sizeof(local));// 數據將來一定會發送到網絡// 端口號和IP地址 本地格式->網絡序列local.sin_family = AF_INET;    // 協議家族local.sin_port = htons(_port); // 端口號// local.sin_addr.s_addr = inet_addr(_ip.c_str()); // IP地址local.sin_addr.s_addr = INADDR_ANY; // IP地址任意綁定int n = bind(_sockfd, (struct sockaddr *)&local, sizeof(local));if (n < 0){LOG(log_level::Fatal) << "bind 失敗!";exit(2);}LOG(log_level::Info) << "bind 成功!" << _sockfd;}void start(){_isrunning = true;while (_isrunning){char buffer[1024];struct sockaddr_in local;socklen_t len = sizeof(local);// 收信息ssize_t n = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&local, &len);if (n > 0) // 成功是收到信息{// 處理從網絡獲取的信息后續交給翻譯InetAddr client(local);buffer[n] = 0;// 講數據回調給外層的函數進行處理,接收返回結果即可std::string ret = _func(buffer, client);// 將處理后的信息發給客戶端ssize_t m = sendto(_sockfd, ret.c_str(), ret.size(), 0, (struct sockaddr *)&local, sizeof(local));}}}private:int _sockfd;// std::string _ip; // IP地址用的字符串風格uint16_t _port; // 端口號bool _isrunning;func_t _func;
};

client.cc

#include <iostream>
#include "server.hpp"
#include <memory>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <string>
#include <string.h>
#include <netinet/in.h>
#include "log.hpp"
#include <arpa/inet.h>// 客戶端知道服務器的IP地址和端口號
int main(int argc, char *argv[])
{if (argc != 3){std::cout << "Usage: ./server IP port" << std::endl;return 1;}using_screen_strategy();std::string ip = argv[1];uint16_t port = std::stoi(argv[2]);// 創建套接字int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0){LOG(log_level::Fatal) << "socket 創建失敗!";exit(1);}LOG(log_level::Info) << "socket 創建成功!" << sockfd;// 客戶端不需要顯示的bind自己的IP地址和端口號// 但是,在首次發送信息的時候,系統會自動綁定IP地址和端口號,端口號是系統隨機分配的// 為什么要這樣做呢??首先,端口號原則上來說只能有一個進程占有,我們的一臺主機上可以同時啟動了多個進程// 比如我先后打開了抖音和快手兩個APP,如果這兩個進程都顯示的綁定系統的端口號,有沒有可能這連個進程所綁定// 端口號是一樣的呢??所以,為了避免端口號沖突,系統會為我們分配隨機未使用的端口號自動綁定。// 填寫服務器信息sockaddr_in local;memset(&local, 0, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(port);local.sin_addr.s_addr = inet_addr(ip.c_str());// 收發送消息while (true){std::string input;std::cout << "請輸入#";std::getline(std::cin, input);// 發送消息int n = sendto(sockfd, input.c_str(), input.size(), 0, (struct sockaddr *)&local, sizeof(local));// 接收消息【有可能連接多個服務器,所以要接收服務器端口號】char buffer[1024];sockaddr_in peer;bzero(&peer, 0);socklen_t len = sizeof(peer);int m = recvfrom(sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &len);if (m > 0){buffer[m] = 0;std::cout << buffer << std::endl;std::cout << "------------------------------\n";}}return 0;
}

dict.hpp

#pragma once#include <iostream>
#include <string>
#include <unordered_map>
#include "log.hpp"using namespace log_module;const std::string default_dict = "./dict.txt";
const std::string sep = ": "; // 分隔符class dict
{
public:dict(const std::string &path = default_dict) : _path(path){}// 加載配置文件bool down_load(){// 打開配置文件std::ifstream in(_path);if (!in.is_open()){LOG(log_level::Error) << "打開字典配置文件失敗!" << " 路徑" << _path;return false;}// 讀取配置文件std::string line;while (std::getline(in, line)){// apple: 蘋果 [[9]]auto pos = line.find(sep);if (pos == std::string::npos) // 沒有找到分隔符{LOG(log_level::Warning) << "加載" << line << "錯誤!" << " 跳過";continue;}std::string English = line.substr(0, pos);std::string Chinese = line.substr(pos + sep.size());if (English.empty() || Chinese.empty()){LOG(log_level::Warning) << "加載數據" << line << "無效!" << " 跳過";continue;}_map.insert(std::make_pair(English, Chinese));LOG(log_level::Debug) << "加載" << line << "成功";}in.close();return true;}// translatestd::string translate(const std::string &dict, InetAddr &peer){// LOG(log_level::Debug) << "走到了翻譯邏輯";auto tmp = _map.find(dict);if (tmp == _map.end()){return "None";}LOG(log_level::Info) << dict << "->" << tmp->second << "[" << peer.IP() << " : " << peer.port() << "]";return tmp->second;}~dict(){}private:std::string _path;                                 // 配置文件的路徑std::unordered_map<std::string, std::string> _map; // 從配置文件加載的字典
};

InetAddr.hpp

#pragma once#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <string>
#include <string.h>
#include <netinet/in.h>
#include "log.hpp"
#include <arpa/inet.h>
#include <functional>class InetAddr
{
public:InetAddr(const sockaddr_in &peer) : _peer(peer){_port = ntohs(_port);            // 端口號_ip = inet_ntoa(_peer.sin_addr); // IP地址->點分十進制}uint16_t port(){return _port;}std::string IP(){return _ip;}~InetAddr(){}private:struct sockaddr_in _peer;uint16_t _port;std::string _ip;
};

dict.txt?

apple: 蘋果 [[9]]
banana: 香蕉 [[9]]
divide: 分割 [[3]]
closed-door policy: 閉關自守 [[11]]
keep healthy: 保持健康 [[11]]
he: 他 [[9]]
countries: 國家 [[9]]
exam: 考試 [[8]]
lab: 實驗室 [[8]]
UNESCO: 聯合國教科文組織 [[8]]
smog: 煙霧 [[8]]
smoke: 煙 [[8]]
fog: 霧 [[8]]
carelessly: 粗心地 [[8]]
re-export: 再出口 [[8]]
undoubtedly: 無疑地 [[8]]
Vast lawns: 寬闊的草坪 [[7]]
gigantic trees: 參天大樹 [[7]]
foliage: 樹葉 [[7]]
extensive: 廣闊的 [[7]]
sheets of vivid green: 鮮艷的綠絨似的氈毯 [[7]]
clumps of gigantic trees: 數株巨樹 [[7]]
heaping up: 聚集 [[7]]
rich piles: 濃密的堆疊 [[7]]
here and there: 這里和那里 [[7]]
fancying himself: 自以為 [[5]]
so great: 非常了不起 [[5]]
walking: 走 [[5]]
fancy: 想象 [[5]]
great: 偉大的 [[5]]
self: 自己 [[5]]
East: 東 [[5]]
West: 西 [[5]]
mouse: 老鼠 [[9]]
photos: 照片 [[9]]
boy: 男孩 [[9]]
swim: 游泳 [[9]]
three: 三 [[9]]?

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

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

相關文章

Quality Control II: Trimming (二):BBDuk

參考&#xff1a;BBDuk Guide - Archive 在我們了解了如何使用trimmomatic之后&#xff0c;我們開始進一步了解另外一種trim工具BBDuk 首先小編要聲明&#xff1a;如果想要完全掌握一個工具是需要較長時間的鉆研和學習的&#xff0c;這里呢只是提供BBDuk處理數據的基本邏輯和…

AlmaLinux8 平替 manylinux_2_28-python 的 GPG密鑰管理、安裝 cuda sdk

0. 下載 AlmaLinux 8 docker 鏡像 https://hub.docker.com/r/almalinux/8-base/tags 下載鏡像&#xff1a; sudo docker pull almalinux/8-base:8.4 創建一個容器&#xff1a; sudo docker run --gpus all -it --name cudaq_src_py_LHL_06 -v /home/hanmeimei/big…

BM1684X平臺:Qwen-2-5-VL圖像/視頻識別應用

一、 簡介 Qwen-2-5-VL 是阿里巴巴通義千問團隊推出的多模態大語言模型&#xff08;MLLM&#xff09;&#xff0c;屬于 Qwen-2 系列模型的一部分&#xff0c;支持視覺&#xff08;Vision&#xff09;與語言&#xff08;Language&#xff09;的多模態交互。 1、特性 動態分辨…

前端項目工程化配置webpack與vite

webpack與vite一、了解 webpack入口(entry)輸出(output)loader插件(plugin)模式(mode)二、項目中使用webpackvue項目react項目三、了解vite構建選項&#xff08;build&#xff09;模塊解析&#xff08;Resolve&#xff09;模塊處理&#xff08;Module&#xff09;服務器選項&am…

機器學習(3):KNN算法-分類

一、KNN算法 K-近鄰算法&#xff08;K-Nearest Neighbors&#xff0c;簡稱KNN&#xff09;,根據K個鄰居樣本的類別來判斷當前樣本的類別&#xff1b;如果一個樣本在特征空間中的k個最相似(最鄰近)樣本中的大多數屬于某個類別&#xff0c;則該類本也屬于這個類別。一些距離&…

Redis Windows遷移方案與測試

我想將開源軟件Redis的主程序和附屬程序遷移到Windows平臺&#xff0c;目前它只能在Linux上運行&#xff0c;讓它可以在Windows 11和Windows Server 2025上運行&#xff0c;這需要考慮Linux操作系統和Windows操作系統的差異&#xff0c;請列舉出將Redis在Linux系統上運行的GCC的…

信息安全概述--實驗總結

數據鏈路層--ARP欺騙ARP欺騙原理XP2要與XP3通信&#xff0c;要發送ARP請求&#xff0c;詢問XP3的MAC地址kali冒充XP3持續給XP2發送ARP應答&#xff0c;XP2會以為收到的MAC地址是XP3的&#xff0c;實際是kali的之后XP2發送的數據都是發給kali的如果說XP2需要想要訪問互聯網&…

【Electron】打包后圖標不變問題,圖標問題

windows上圖標未更換。圖標已經換了&#xff0c;但新打出的包或是安裝后的 exe 圖標沒有更換。這個時候可以右擊你的exe或是安裝包點屬性&#xff0c;看看圖標是否正常&#xff0c;如果這里的圖標正常&#xff0c;那其實就是成功的了。主要原因是因為 windows 圖標緩存機制導致…

單詞拆分 II

題目&#xff1a;思考&#xff1a; 本質上和單詞拆分1沒什么區別單詞拆分1是問能不能拆單詞拆分2是問把所有拆的方案列出來要列出所有方案&#xff0c;采用字典樹回溯 實現&#xff1a; class Node { public:vector<Node*> check;bool isEnd;Node(int num){for (int i0;i…

國產三防平板電腦是什么?三防平板推薦

國產三防平板電腦&#xff0c;專為應對極端工作環境而生。這類設備集防水、防塵、防摔三大防護性能于一體&#xff0c;通過IP67/IP68防護認證及MIL-STD-810軍規標準測試&#xff0c;能在建筑工地、油田勘探、應急救援等惡劣場景中穩定運行。其核心價值在于將消費級平板的智能體…

優思學院|什么是精益生產管理?原則與方法詳述

在企業經營中&#xff0c;「利潤&#xff1d;價格&#xff0d;成本」這條公式可謂家喻戶曉。傳統的成本思維通常認為價格由公司設定&#xff0c;而成本則是難以撼動的既定事實。然而&#xff0c;隨著市場經濟與自由定價機制的成熟&#xff0c;企業逐漸意識到——價格其實是由市…

【銀行測試】銀行票據項目業務+票據測試點分析(四)

目錄&#xff1a;導讀 前言一、Python編程入門到精通二、接口自動化項目實戰三、Web自動化項目實戰四、App自動化項目實戰五、一線大廠簡歷六、測試開發DevOps體系七、常用自動化測試工具八、JMeter性能測試九、總結&#xff08;尾部小驚喜&#xff09; 前言 1、提示付款 功能…

基于華為開發者空間的Open WebUI數據分析與可視化實戰

1 概述 1.1 案例介紹 本案例演示如何在華為開發者空間云主機上搭建Open WebUI環境&#xff0c;結合DeepSeek-R1模型進行數據分析、統計建模、數據可視化和業務洞察挖掘等實際數據科學任務。 1.2 適用對象 數據分析師業務分析師數據科學工程師市場研究人員統計學專業學生 1…

【HZ-T536開發板免費體驗】Cangjie Magic調用視覺語言大模型(VLM)真香,是不是可以沒有YOLO和OCR了?

目錄 引言 編寫視覺語言大模型&#xff08;VLM&#xff09;程序 交叉編譯Cangjie Magic到T536開發板 對cjpm.toml文件的修改 stdx庫的配置 拷貝libsecurec.so到cangjie的庫文件中 開始交叉編譯 部署到開發板 拷貝所需要的庫文件 安裝curl 運行程序 結束語 本文首發…

最長連續序列(每天刷力扣hot100系列)

目錄 題目介紹&#xff1a; 哈希表法&#xff1a; 復雜度分析&#xff1a; 思路分析&#xff1a; unordered_set 和 unordered_map的比較&#xff1a; 1. 核心區別 2. 使用場景 3. 在本題中的選擇 4. 性能對比 5. 成員函數差異 unordered_table.begin()函數是返回的鍵…

國標渠道研究:專業為渠道策略提供數據支持(渠道調研)

北京國標市場調查有限公司是一家專業的市場調查公司&#xff0c;&#xff08;線上問卷調查&#xff09;&#xff08;第三方市場咨詢&#xff09;&#xff08;消費者調查研究&#xff09;專注于為企業提供全方位的渠道研究服務。服務范圍包括渠道策略研究、渠道銷售數據分析和渠…

深入理解 C 語言中的拷貝函數

目錄1. C 語言中的主要拷貝函數2. strcpy&#xff1a;字符串拷貝函數簽名示例局限性3. strncpy&#xff1a;指定長度的字符串拷貝函數簽名示例局限性4. memcpy&#xff1a;通用內存拷貝函數簽名示例優勢局限性5. memmove&#xff1a;支持重疊內存拷貝函數簽名示例優勢局限性6. …

主數據變更流程

主數據&#xff08;如客戶、供應商、產品等&#xff09;的變更流程&#xff08;新增、更新、停用等&#xff09;是主數據管理&#xff08;MDM&#xff09;的核心環節&#xff0c;其設計需兼顧數據質量&#xff08;準確性、一致性&#xff09;、業務合規&#xff08;審批權限、審…

VUE2 學習筆記 合集

???????VUE2 學習筆記1 VUE特點、開發者工具、入門Demo-CSDN博客 VUE2 學習筆記2 數據綁定、數據代理、MVVM_vue2的數據綁定-CSDN博客 VUE2 學習筆記3 v-on、事件修飾符、鍵盤事件_vue2組件 點擊事件-CSDN博客 VU2 學習筆記4 計算屬性、監視屬性-CSDN博客 VUE2 學習…

【motion】HumanML3D 的安裝1:環境搭建

https://github.com/EricGuo5513/HumanML3D/issues/10 (base) root@k8s-master-pfsrv:/home/zhangbin/perfwork/01_ai/15_HumanML3D# conda env create -f environment.yaml Retrieving notices: ...working... done Channels:- defaults Platform: linux-64 Collecting