柚子快報(bào)邀請碼778899分享:c++ UDP英譯漢網(wǎng)絡(luò)詞典
柚子快報(bào)邀請碼778899分享:c++ UDP英譯漢網(wǎng)絡(luò)詞典
這里我們用UDP實(shí)現(xiàn)一個(gè)簡單的英譯漢小詞典。我們還是仿照前一篇的UDP編程,將各自的組件封裝起來,實(shí)現(xiàn)高內(nèi)聚低耦合。
一. 字典翻譯功能實(shí)現(xiàn)
首先我們將我們的字典知識庫放在txt文本中。
apple: 蘋果 banana: 香蕉 cat: 貓 dog: 狗 book: 書 pen: 筆 happy: 快樂的 sad: 悲傷的 run: 跑 jump: 跳 teacher: 老師 student: 學(xué)生 car: 汽車 bus: 公交車 love: 愛 hate: 恨 hello: 你好 goodbye: 再見 summer: 夏天 winter: 冬天
然后我們來實(shí)現(xiàn)翻譯功能。為了體現(xiàn)高內(nèi)聚低耦合的思想,我們?nèi)匀环庋b成一個(gè)類。
const string defaultpath="./Dict.txt";
class Dict
{
public:
Dict(const string& path=defaultpath)
:_dict_conf_filepath(path)
{}
~Dict()
{}
private:
unordered_map
string _dict_conf_filepath;
};
可以看到,類成員有兩個(gè),一個(gè)是Map類型的,對應(yīng)我們先前txt文本中的一些漢英對照單詞;另一個(gè)是string類型的,表明我們應(yīng)該去哪里找漢英對照。
初始化時(shí)我們應(yīng)該根據(jù)txt文本中的中英單詞,填充_dict成員。
創(chuàng)建函數(shù):
const string sep=": ";
bool Load()
{
ifstream in(_dict_conf_filepath);
if(!in.is_open())
{
LOG(FATAL,"open %s error\n",_dict_conf_filepath);
return false;
}
string line;
while(getline(in,line))
{
if(line.empty()) continue;
auto pos=line.find(sep);//[)
if(pos==string::npos) continue;
string word=line.substr(0,pos);
if(word.empty()) continue;
string han=line.substr(pos+sep.size());
if(han.empty()) continue;
LOG(DEBUG,"load info, %s: %s\n",word,han);
_dict.insert(make_pair(word,han));
}
in.close();
LOG(DEBUG,"load %s success\n",_dict_conf_filepath.c_str());
return true;
}
那么當(dāng)我們上層調(diào)用函數(shù)尋找時(shí),就可以根據(jù)_dict成員中找結(jié)果。
string Translate(const string& word,bool &ok)
{
ok=true;
auto iter=_dict.find(word);
if(iter==_dict.end())
{
ok=false;
return "未找到";
}
return iter->second;
}
最后我們加上命名空間,由此我們翻譯功能實(shí)現(xiàn)代碼整體如下:
#pragma once
#include
#include
#include
#include
#include"Log.hpp"
using namespace std;
namespace dict_ns
{
const string defaultpath="./Dict.txt";
const string sep=": ";
class Dict
{
private:
bool Load()
{
ifstream in(_dict_conf_filepath);
if(!in.is_open())
{
LOG(FATAL,"open %s error\n",_dict_conf_filepath);
return false;
}
string line;
while(getline(in,line))
{
if(line.empty()) continue;
auto pos=line.find(sep);//[)
if(pos==string::npos) continue;
string word=line.substr(0,pos);
if(word.empty()) continue;
string han=line.substr(pos+sep.size());
if(han.empty()) continue;
LOG(DEBUG,"load info, %s: %s\n",word,han);
_dict.insert(make_pair(word,han));
}
in.close();
LOG(DEBUG,"load %s success\n",_dict_conf_filepath.c_str());
return true;
}
public:
Dict(const string& path=defaultpath)
:_dict_conf_filepath(path)
{
Load();
}
string Translate(const string& word,bool &ok)
{
ok=true;
auto iter=_dict.find(word);
if(iter==_dict.end())
{
ok=false;
return "未找到";
}
return iter->second;
}
~Dict()
{}
private:
unordered_map
string _dict_conf_filepath;
};
}
二. 服務(wù)端代碼實(shí)現(xiàn)
我們將服務(wù)端封裝成一個(gè)類,并封裝對應(yīng)步驟在類函數(shù)中。
const static int defaultfd = -1;
using func_t=function
class UdpServer
{
public:
UdpServer(uint16_t port,func_t func)
: _sockfd(defaultfd), _port(port), _func(func)
,_isrunning(false)
{}
~UdpServer()
{}
private:
int _sockfd;
uint16_t _port; // 服務(wù)器所用的端口號
bool _isrunning;
//給服務(wù)器設(shè)定回調(diào),用來讓上層進(jìn)行注冊業(yè)務(wù)的處理方法
func_t _func;
};
此處有一個(gè)自定義類型func_t的變量,我們觀察其參數(shù)結(jié)構(gòu),可以發(fā)現(xiàn)其實(shí)就是我們上面實(shí)現(xiàn)的翻譯功能類中的Translate函數(shù)。我們通過這樣的方式,實(shí)現(xiàn)高內(nèi)聚低耦合,讓上層實(shí)現(xiàn)翻譯功能。
此處服務(wù)端同樣不需要IP地址,與前面原因相同(不知道的同鞋可以看links)。
服務(wù)端初始成員函數(shù):
void InitServer()
{
// 1.創(chuàng)建udp socket 套接字...必須要做的
_sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (_sockfd < 0)
{
LOG(FATAL, "socket error,%s,%d\n", strerror(errno), errno);
exit(SOCKET_ERROR);
}
LOG(INFO, "socket create success,sockfd: %d\n", _sockfd);
// 2.1 填充sockaddr_in結(jié)構(gòu)
struct sockaddr_in local; // struct sockaddr_in 系統(tǒng)提供的數(shù)據(jù)類型,local是變量,用戶棧上開辟空間
bzero(&local, sizeof(local)); // 清空
local.sin_family = AF_INET;
local.sin_port = htons(_port); // port要經(jīng)過網(wǎng)絡(luò)傳輸給對面,即port先到網(wǎng)絡(luò),所以要將_port,從主機(jī)序列轉(zhuǎn)化為網(wǎng)絡(luò)序列
local.sin_addr.s_addr=INADDR_ANY;//htonl(INADDR_ANY)
// 2.2 bind sockfd和網(wǎng)絡(luò)信息(IP(?)+Port)
int n = bind(_sockfd,(struct sockaddr*)&local,sizeof(local));
if(n<0)
{
LOG(FATAL, "bind error,%s,%d\n", strerror(errno), errno);
exit(BIND_ERROR);
}
LOG(INFO, "socket bind success\n");
}
此處還是跟前面UDP編程一樣。
服務(wù)端啟動(dòng)成員函數(shù):
void Start()//所有的服務(wù)器,本質(zhì)解決的是輸入輸出的問題!不想讓網(wǎng)絡(luò)通信模塊和業(yè)務(wù)模塊進(jìn)行強(qiáng)耦合
{
//一直運(yùn)行,直到管理者不想運(yùn)行了,服務(wù)器都是死循環(huán)
_isrunning=true;
while(true)
{
char request[1024];
struct sockaddr_in peer;
socklen_t len=sizeof(peer);
//1.我們要讓server先收數(shù)據(jù)
ssize_t n=recvfrom(_sockfd,request,sizeof(request)-1,0,(struct sockaddr*)&peer,&len);
if(n>0)
{
request[n]=0;
InetAddr addr(peer);
LOG(DEBUG,"get message from [%s:%d]: %s\n",addr.Ip().c_str(),addr.Port(),request);
bool ok;
string response=_func(request,ok);//將請求回調(diào)出去,在外部進(jìn)行處理
(void)ok;
//2.我們要將server收到的數(shù)據(jù),發(fā)回給對方
sendto(_sockfd,response.c_str(),response.size(),0,(struct sockaddr*)&peer,len);
}
}
_isrunning=false;
}
此處我們大致思路還是先通過recvfrom函數(shù)收到來自客戶端的數(shù)據(jù),然后再將翻譯的結(jié)果返回給客戶端。在這中間,就是我們的翻譯函數(shù),在服務(wù)端類中即我們的_func成員。
那么服務(wù)端代碼合起來就是:
#pragma once
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "Log.hpp"
#include"InetAddr.hpp"
#include"Dict.hpp"
using namespace std;
enum
{
SOCKET_ERROR = 1,
BIND_ERROR,
USAGE_ERROR
};
const static int defaultfd = -1;
using func_t=function
class UdpServer
{
public:
UdpServer(uint16_t port,func_t func)
: _sockfd(defaultfd), _port(port), _func(func)
,_isrunning(false)
{}
void InitServer()
{
// 1.創(chuàng)建udp socket 套接字...必須要做的
_sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (_sockfd < 0)
{
LOG(FATAL, "socket error,%s,%d\n", strerror(errno), errno);
exit(SOCKET_ERROR);
}
LOG(INFO, "socket create success,sockfd: %d\n", _sockfd);
// 2.1 填充sockaddr_in結(jié)構(gòu)
struct sockaddr_in local; // struct sockaddr_in 系統(tǒng)提供的數(shù)據(jù)類型,local是變量,用戶棧上開辟空間
bzero(&local, sizeof(local)); // 清空
local.sin_family = AF_INET;
local.sin_port = htons(_port); // port要經(jīng)過網(wǎng)絡(luò)傳輸給對面,即port先到網(wǎng)絡(luò),所以要將_port,從主機(jī)序列轉(zhuǎn)化為網(wǎng)絡(luò)序列
local.sin_addr.s_addr=INADDR_ANY;//htonl(INADDR_ANY)
// 2.2 bind sockfd和網(wǎng)絡(luò)信息(IP(?)+Port)
int n = bind(_sockfd,(struct sockaddr*)&local,sizeof(local));
if(n<0)
{
LOG(FATAL, "bind error,%s,%d\n", strerror(errno), errno);
exit(BIND_ERROR);
}
LOG(INFO, "socket bind success\n");
}
void Start()//所有的服務(wù)器,本質(zhì)解決的是輸入輸出的問題!不想讓網(wǎng)絡(luò)通信模塊和業(yè)務(wù)模塊進(jìn)行強(qiáng)耦合
{
//一直運(yùn)行,直到管理者不想運(yùn)行了,服務(wù)器都是死循環(huán)
_isrunning=true;
while(true)
{
char request[1024];
struct sockaddr_in peer;
socklen_t len=sizeof(peer);
//1.我們要讓server先收數(shù)據(jù)
ssize_t n=recvfrom(_sockfd,request,sizeof(request)-1,0,(struct sockaddr*)&peer,&len);
if(n>0)
{
request[n]=0;
InetAddr addr(peer);
LOG(DEBUG,"get message from [%s:%d]: %s\n",addr.Ip().c_str(),addr.Port(),request);
bool ok;
string response=_func(request,ok);//將請求回調(diào)出去,在外部進(jìn)行處理
(void)ok;
//2.我們要將server收到的數(shù)據(jù),發(fā)回給對方
sendto(_sockfd,response.c_str(),response.size(),0,(struct sockaddr*)&peer,len);
}
}
_isrunning=false;
}
~UdpServer()
{
}
private:
int _sockfd;
uint16_t _port; // 服務(wù)器所用的端口號
bool _isrunning;
//給服務(wù)器設(shè)定回調(diào),用來讓上層進(jìn)行注冊業(yè)務(wù)的處理方法
func_t _func;
};
三. 服務(wù)端調(diào)用實(shí)現(xiàn)
此處還是跟UDP編程一樣,因?yàn)槲覀儗?shí)際只在服務(wù)端代碼內(nèi)部作了改動(dòng),在外層看起來調(diào)用都是沒變的。
#include
#include
#include"UdpServer.hpp"
#include"Log.hpp"
#include"Dict.hpp"
using namespace std;
using namespace dict_ns;
void Usage(string proc)
{
cout<<"Usage:\n\t"< } // ./udpserver ip int main(int argc,char *argv[]) { if(argc!=2) { Usage(argv[0]); exit(USAGE_ERROR); } EnableScreen(); //string ip=argv[1]; //定義翻譯模塊 Dict dict; //網(wǎng)絡(luò)模塊 uint16_t port=stoi(argv[1]); unique_ptr bind(&Dict::Translate,&dict,placeholders::_1,placeholders::_2));//C++14 usvr->InitServer(); usvr->Start(); return 0; } 四. 客戶端代碼實(shí)現(xiàn) 此處也是沒有變化的,所以我們可以體會到我們這種將不同功能的代碼分別封裝起來思想的好處??梢钥吹酱颂幐鶸DP編程其實(shí)變化不大。 #include #include #include #include #include #include #include #include #include using namespace std; void Usage(string proc) { cout<<"Usage:\n\t"< } // ./udpclient serverip serverport int main(int argc,char *argv[]) { if(argc!=3) { Usage(argv[0]); exit(1); } string serverip=argv[1]; uint16_t serverport=stoi(argv[2]); //1.創(chuàng)建socket int sockfd=socket(AF_INET,SOCK_DGRAM,0); if(sockfd<0) { cerr<<"socket error"< } //2.client一定要bind,client也有自己的ip和port,但是不建議顯示(和server一樣用bind函數(shù))bind //a.那如何bind呢?當(dāng)udp client首次發(fā)送數(shù)據(jù)的時(shí)候,os會自動(dòng)隨機(jī)的給client進(jìn)行bind--為什么?要bind,必然要和port關(guān)聯(lián)!防止client port沖突 //b.什么時(shí)候bind?首次發(fā)送數(shù)據(jù)的時(shí)候 //構(gòu)建目標(biāo)主機(jī)的socket信息 struct sockaddr_in server; memset(&server,0,sizeof(server)); server.sin_family=AF_INET; server.sin_port=htons(serverport); server.sin_addr.s_addr=inet_addr(serverip.c_str()); string message; //3.直接通信即可 while(true) { cout<<"Please Enter# "; getline(cin,message); sendto(sockfd,message.c_str(),message.size(),0,(struct sockaddr*)&server,sizeof(server)); struct sockaddr_in peer; socklen_t len=sizeof(peer); char buffer[1024]; ssize_t n=recvfrom(sockfd,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&peer,&len); if(n>0) { buffer[n]=0; cout<<"server echo# "< } } return 0; } 五. 效果展示 此處打印出來的英漢對照有點(diǎn)格式問題,所以沒有顯示出來,但是我們可以發(fā)現(xiàn)翻譯出來是沒有問題的。 總結(jié): 好了,到這里今天的知識就講完了,大家有錯(cuò)誤一點(diǎn)要在評論指出,我怕我一人擱這瞎bb,沒人告訴我錯(cuò)誤就寄了。 祝大家越來越好,不用關(guān)注我(瘋狂暗示) 柚子快報(bào)邀請碼778899分享:c++ UDP英譯漢網(wǎng)絡(luò)詞典 推薦閱讀
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場。
轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。