关于拼写小助手的开发日记
目录
---恢复内容开始---
我的spellcorrect 开发文档
相关配置文件及准备工作:
我的主要文件夹分为三个:分别为客户端,cppjieba分词库,离线部分及服务器部分
客户端部分:内部为客户端源码及运行程序
cppjieba分词库部分,就不赘述,请自行安装
离线部分:内部有中英文件夹放置索引及词典文件,还有配置文件及分词库,其余为代码
服务器部分:最为重要的配置文件及数据部分,头文件在include,实现文件在src里面
演示效果(中文):
==启动时服务器:==
==客户端连入时时服务器:==
==输入“赵”==
==输入“周杰伦”==
==输入清华大学==
演示效果(英语):
==启动时服务器:==
==客户端连入时时服务器:==
==输入student:==
==输入spell:==
==输入computer:==
代码部分:
离线部分:
==makefile:==
srcs:=$(wildcard *.cc) objs:= $(patsubst %.cc, %.o, $(srcs)) cxx:=g++ cxxflags:= -w -g -std=c++11 $(addprefix -i, $(inc_dir)) $(libs) -wno-deprecated -i ../cppjieba/include/ -i ../cppjieba/deps exe:=./main $(exe):$(objs) $(cxx) -o $(exe) $(objs) $(cxxflags) clean: rm -rf $(exe) rm -rf $(objs)
==configuration:==
///======================================= /// file: configuration.h /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-12 00:30:39 /// dream: don't forget your dreams! /// ====================================== #ifndef __configuration_h__ #define __configuration_h__ #include "nocopyable.h" #include <string> #include <map> using namespace std; class configuration :public noncopyable { public: configuration(const string &filepath); string getenglishdir() const {return _englishdir;} string getchinesedir() const {return _chinesedir;} private: string _filepath; string _englishdir; string _chinesedir; }; template<typename t> class singleton { public: template<typename ...args> static t* getinstance(args ...args) { if(!_pinstance) _pinstance = new t(args...); return _pinstance; } static void destroy() { if(_pinstance) delete _pinstance; } private: singleton(); ~singleton(); static t *_pinstance; }; template<typename t> t * singleton<t>::_pinstance = null; #endif
1 ///======================================= 2 /// file: configuration.cc 3 /// author: wtptorres(1584292712@qq.com) 4 /// date: 2019-06-12 00:30:04 5 /// dream: don't forget your dreams! 6 /// ====================================== 7 8 9 #include "configuration.h" 10 #include <utility> 11 #include <fstream> 12 #include <iostream> 13 using namespace std; 14 15 configuration::configuration(const string &filepath) 16 :_filepath(std::move(filepath)) 17 { 18 ifstream ifs(_filepath); 19 if(!ifs) 20 cout<<"file open error!"<<endl; 21 ifs>>_englishdir; 22 ifs>>_chinesedir; 23 ifs.close(); 24 }
///======================================= /// file: nocopyable.h /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-12 00:24:36 /// dream: don't forget your dreams! /// ====================================== #ifndef __nocopyable_h__ #define __nocopyable_h__ class noncopyable { public: noncopyable()=default; ~noncopyable()=default; private: noncopyable(const noncopyable &rhs); noncopyable &operator =(const noncopyable &rhs); }; #endif
==dictproducer:==
///======================================= /// file: dictproducer.h /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-12 16:40:25 /// dream: don't forget your dreams! /// ====================================== #ifndef __dictproducer_h__ #define __dictproducer_h__ #include "splittool.h" using namespace std; #include "nocopyable.h" #include <memory> #include <string> #include <vector> #include <map> #include <utility> class dictproducer :public noncopyable { public: dictproducer(const string,const string,const string &,splittooljieba *); ~dictproducer(){} void build_dict(); void build_cn_dict(); void store_dict(); vector<pair<string,int>>& getindict(){return _indict;} private: void processenglishword(string &word); void processchineseword(string &word);//除去中文的数字 void construct_indict(); string _englishdir; string _chinesedir; string _golefilepath; vector<string> _englishfiles; vector<string> _chinesefiles; map<string,int> _dict; vector<pair<string,int>> _indict; shared_ptr<splittooljieba> _splittool; }; #endif
///======================================= /// file: dictproducer.cc /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-12 16:50:46 /// dream: don't forget your dreams! /// ====================================== #include "dictproducer.h" #include <cctype> #include <utility> #include <fstream> #include <iostream> #define firstsize 10000 using namespace std; dictproducer::dictproducer(const string englishdir,const string chinesedir,const string &golefilepath,splittooljieba *splittoolptr) :_englishdir(englishdir) ,_chinesedir(chinesedir) ,_golefilepath(golefilepath) { _splittool.reset(splittoolptr); std::ifstream ifsenglish(_englishdir); std::ifstream ifschinese(_chinesedir); string filepath; if(!ifsenglish || !ifschinese){ cout<<"dict file open error!"<<endl; } while(ifsenglish>>filepath) { _englishfiles.push_back(filepath); } while(ifschinese>>filepath) { _chinesefiles.push_back(filepath); } _indict.reserve(firstsize); } void dictproducer::processenglishword(string &word) { auto cit =word.begin(); for(;cit!=word.end();++cit) { //去除标点符号或数字 if(!isalpha(*cit)){ word.erase(cit); --cit;//迭代器位置发生改变 }else if(isupper(*cit)){//将大写字母改为小写 *cit =tolower(*cit); } } } void dictproducer::processchineseword(string &word) { auto cit =word.begin(); for(;cit!=word.end();++cit) { //去除数字 if(!isalnum(*cit)){ word.erase(cit); --cit; } } } void dictproducer::build_dict()//建立英文词典 { for(auto &filepath:_englishfiles) { ifstream ifs(filepath); if(!ifs){ cout<<"english file open error!"<<endl; } string word; while(ifs>>word) { processenglishword(word); auto cit =_dict.find(word); if(word.size()>0 && cit ==_dict.end()){ _dict.insert(std::make_pair(word,1)); }else if(cit!=_dict.end()){ ++ cit ->second; } } } } void dictproducer::build_cn_dict() { vector<string>words; for(auto filepath:_chinesefiles) { ifstream ifs(filepath); if(!ifs){ cout<<"chinese file open error!"<<endl; } string sentence; while(std::getline(ifs,sentence)) { _splittool->cut(sentence); } } vector<string>& results =_splittool->getresult(); for(auto &res:results) { processchineseword(res); auto cit =_dict.find(res); if(cit ==_dict.end()){ _dict.insert(std::make_pair(res,1)); }else{ ++ cit ->second; } } } void dictproducer::store_dict() { construct_indict(); ofstream ofs(_golefilepath); if(!ofs) cout<<"store_dict open file error!"<<endl; for(auto &mypair:_indict) { ofs<<mypair.first<<" "<<mypair.second<<endl; } ofs.close(); } void dictproducer::construct_indict() { for(auto dictpair:_dict){ _indict.push_back(dictpair); } }
==getindex:==
///======================================= /// file: getindex.h /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-12 08:52:04 /// dream: don't forget your dreams! /// ====================================== #ifndef __getindex_h__ #define __getindex_h__ #include <string> #include <unordered_map> #include <set> #include <vector> #include <utility> #include <unordered_set> using namespace std; class getindex { public: getindex(const string &,const string &,const string &); ~getindex(){} void construct_index(); void store_index(); private: bool isenglish(const string &rhs) const; vector<string>getonecharacter(const string & word); string _sourcefilepath; string _golefilepath; string _stopwordsfilepath; vector<pair<string,int>>_dict; unordered_set<string>_stopwords; unordered_map<string,set<int>>_index; }; #endif
///======================================= /// file: getindex.cc /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-12 09:00:11 /// dream: don't forget your dreams! /// ====================================== #include "getindex.h" #include <fstream> #include <sstream> #include <iostream> using namespace std; getindex::getindex(const string & sourcefilepath,const string &golefilepath,const string &stopwordsfilepath) :_sourcefilepath(std::move(sourcefilepath)) ,_golefilepath(std::move(golefilepath)) ,_stopwordsfilepath(std::move(stopwordsfilepath)) { ifstream ifs(_sourcefilepath); if(!ifs){ cout<<"getindex file open error!"<<endl; } string line; while(getline(ifs,line)) { istringstream iss(line); string key; int value; iss>>key>>value; _dict.push_back(std::make_pair(key,value)); } ifstream ifs1(_stopwordsfilepath); if(!ifs1){ cout<<"file open error!"<<endl; } string stopword; while(ifs1>>stopword,!ifs1.eof()) { _stopwords.insert(stopword); } } vector<string> getindex::getonecharacter(const string &word) { vector<string>tmp; auto cit =word.begin(); while(cit<word.end()) { if(224==(*cit &224)) { string onecharacter; onecharacter.append(cit,cit+3); tmp.push_back(onecharacter); cit +=3; }else if(240==(*cit &240)){ string onecharacter; onecharacter.append(cit,cit+4); tmp.push_back(onecharacter); cit +=4; }else break; } return tmp; } bool getindex::isenglish(const string &rhs) const { char ch =*(rhs.begin()); if(ch<0) return false; return true; } #if 0 bool getindex::isenglish(const string &rhs) const { char ch =*(rhs.begin()); if(ch<0){ return false; } return true; } #endif void getindex::construct_index() { for(size_t i=0;i!=_dict.size();++i) { string tmp=_dict[i].first; if(isenglish(tmp)) { for(auto ch:tmp) { string charactor(1,ch); if(isalpha(ch)) { auto cit =_index.find(charactor); if(cit ==_index.end()) { set<int> smp; smp.insert(i); _index.insert(std::make_pair(charactor,smp)); }else{//已经存在了该字母的索引 cit->second.insert(i); } } } }else{//中文处理部分 vector<string> onecharacterrally =getonecharacter(tmp); for(auto onecharacter:onecharacterrally) {//stop_words中不存在该单词,则加入索引中 if(_stopwords.find(onecharacter)==_stopwords.end()){ auto it =_index.find(onecharacter); if(it == _index.end()){ set<int>tmp; tmp.insert(i); _index.insert(std::make_pair(onecharacter,tmp)); }else{ it->second.insert(i); } } } } } } void getindex::store_index() { //ofs存储索引的内容 std::ofstream ofs(_golefilepath); if(!ofs){ cout<<"file open error!"<<endl; return; } for(auto data:_index) { ofs<<data.first<<" "; for(auto linenum:data.second) { ofs<<linenum<<" "; } ofs<<endl; } ofs.close(); }
==splittool:==
///======================================= /// file: splittool.h /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-12 17:12:01 /// dream: don't forget your dreams! /// ====================================== #ifndef __splittool_h__ #define __splittool_h__ #include "../cppjieba/include/cppjieba/jieba.hpp"//需要自己将cppjieba安装在项目目录下 #include "configuration.h" #include <string> #include <vector> using namespace std; using namespace cppjieba; class splittooljieba { public: splittooljieba(const string& dict_path,const string &model_path,const string &user_dict_path,const string & idfpath, const string &stopwordpath) :_jieba(dict_path,model_path,user_dict_path,idfpath,stopwordpath) {} ~splittooljieba(){} void cut(const string & sentence) { vector<string>tmp; _jieba.cut(sentence,tmp); _result.insert(_result.end(),tmp.begin(),tmp.end()); } vector<string> & getresult(){return _result;} private: vector<string> _result; cppjieba::jieba _jieba; }; #endif
==main:==
///======================================= /// file: main.cc /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-12 20:38:50 /// dream: don't forget your dreams! /// ====================================== #include "configuration.h" #include "splittool.h" using namespace std; #include "dictproducer.h" #include "getindex.h" #include <iostream> #include <memory> const char * const dict_path ="../cppjieba/dict/jieba.dict.utf8"; const char * const hmm_path ="../cppjieba/dict/hmm_model.utf8"; const char * const user_dict_path ="../cppjieba/dict/user.dict.utf8"; const char * const idf_path ="../cppjieba/dict/idf.utf8"; const char * const stop_word_path ="../cppjieba/dict/stop_words.utf8"; const string gole_dict_path="../server/data/dict.txt"; const string gole_index_path="../server/data/index.txt"; class splittool; int main(void) { configuration *pconfig =singleton<configuration>::getinstance("configure.txt"); splittooljieba *ptool =new splittooljieba(dict_path,hmm_path,user_dict_path,idf_path,stop_word_path); dictproducer mydictproducer(pconfig->getenglishdir(),pconfig->getchinesedir(),gole_dict_path,ptool); mydictproducer.build_dict();//建立英语词典 mydictproducer.build_cn_dict();//建立中文词典 mydictproducer.store_dict();//储存词典 getindex myindex(gole_dict_path,gole_index_path,"stop_words_zh.txt"); myindex.construct_index();//建立索引 myindex.store_index();//存储索引 singleton<configuration>::destroy(); return 0; }
在线部分:
==configuration:==
///======================================= /// file: configuration.cc /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-06 10:32:43 /// dream: don't forget your dreams! /// ====================================== #ifndef __configuration_h__ #define __configuration_h__ #include "noncopyable.h" #include <string> #include <map> #define confpath "/home/wtp/spell/server/conf/configure.txt" using namespace std; namespace wd { class configuration :public noncopyable { public: configuration(const string &filepath); ~configuration()=default; string getdictdir() const; string getindexdir() const; string getip()const; string getcache() const; unsigned short getport() const; private: string _filepath; map<string,string> _conf; }; }; template<typename t> class singleton { public: template<typename ...args> static t *getinstance(args ...args) { if(!_pinstance) _pinstance=new t(args ...); return _pinstance; } static void destroy() { if(_pinstance) delete _pinstance; } private: singleton(); ~singleton(); static t *_pinstance; }; template<typename t> t *singleton<t>::_pinstance =null; #endif
///======================================= /// file: configuration.cc /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-07 15:24:14 /// dream: don't forget your dreams! /// ====================================== #include "configuration.h" #include <stdlib.h> #include <utility> #include <fstream> #include <iostream> using namespace std; using namespace wd; wd::configuration::configuration(const string & filepath) :_filepath(std::move(filepath)) { ifstream ifs(_filepath); if(!ifs){ cout<<"file open error!"<<endl; } string key,value; while(ifs>>key) { ifs>>value; _conf.insert(std::make_pair(key,value)); } ifs.close(); } string wd::configuration::getdictdir() const { auto cit=_conf.find("mydict"); if(cit== _conf.end()) return ""; else return cit->second; } string wd::configuration::getindexdir() const { auto cit =_conf.find("myindex"); if(cit== _conf.end()) return ""; else return cit->second; } string wd::configuration::getip() const { auto cit =_conf.find("myip"); if(cit ==_conf.end()) return ""; else return cit->second; } unsigned short wd::configuration::getport() const { auto cit =_conf.find("myport"); if(cit==_conf.end()) return 0; else return atoi(cit->second.c_str()); } string wd::configuration::getcache() const { auto cit =_conf.find("mycache"); if(cit ==_conf.end()) return ""; else return cit->second; }
==acceptor:==
///======================================= /// file: acceptor.h /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-05 23:47:05 /// dream: don't forget your dreams! /// ====================================== #ifndef __wd_acceptor_h__ #define __wd_acceptor_h__ #include "socket.h" #include "inetaddress.h" namespace wd { class acceptor { public: acceptor(int listenfd,const inetaddress & addr); void ready();//服务器监听准备 int accept();//服务器接收新连接 int fd()const {return _listensock.fd();} private: void setreuseaddr(bool on);//设置地址重用 void setreuseport(bool on);//设置端口重用 void bind();//绑定 void listen();//监听 socket _listensock; inetaddress _addr; }; } #endif
///======================================= /// file: acceptor.cc /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-05 23:52:12 /// dream: don't forget your dreams! /// ====================================== #include <iostream> #include "acceptor.h" #include "socketutil.h" namespace wd { acceptor::acceptor(int listenfd,const inetaddress & addr) :_listensock(listenfd) ,_addr(addr) {} void acceptor::ready() { setreuseaddr(true); setreuseport(true); bind(); listen(); } int acceptor::accept() { int peerfd=::accept(_listensock.fd(),null,null); if(-1==peerfd) { perror("accept error!"); } return peerfd; } void acceptor::setreuseaddr(bool flag) { int on=(flag?1:0); if(::setsockopt(_listensock.fd() ,sol_socket ,so_reuseaddr ,&on ,static_cast<socklen_t>(size_t(on)))==-1) { perror("setsockopt reuseaddr error!"); ::close(_listensock.fd()); exit(exit_failure); } } void acceptor::setreuseport(bool flag) { #ifndef so_reuseport int on=(flag?1:0); if(::setsockopt(_listensock.fd() ,sol_socket ,so_reuseaddr ,&on ,static_cast<socklen_t>(size_t(on)))==-1) { perror("setsockopt reuseport error!"); ::close(_listensock.fd()); exit(exit_failure); } #else if(flag) { fprintf(stderr,"so_reuseport is not supported!\n"); } #endif } void acceptor::bind() { if(-1==::bind(_listensock.fd() ,(const struct sockaddr*)_addr.getsockaddrptr() ,sizeof(inetaddress))) { perror("bind error!"); ::close(_listensock.fd()); exit(exit_failure); } } void acceptor::listen() { if(-1==::listen(_listensock.fd(),10)) { perror("listen error!"); ::close(_listensock.fd()); exit(exit_failure); } } } #if 0 int main() { std::cout<<"acceptor is correct!"<<std::endl; } #endif
==condition.h:==
#ifndef __wd_condition_h__ #define __wd_condition_h__ #include "noncopyable.h" #include "mutexlock.h" #include <pthread.h> namespace wd { class condition :noncopyable { public: condition(mutexlock &mutex) :_mutex(mutex) {pthread_cond_init(&_cond,null);} ~condition() {pthread_cond_destroy(&_cond);} void wait() {pthread_cond_wait(&_cond,_mutex.getmutexlockptr());} void notify() {pthread_cond_signal(&_cond);} void notifyall() {pthread_cond_broadcast(&_cond);} private: pthread_cond_t _cond; mutexlock & _mutex; }; } #endif
==cache:==
///======================================= /// file: cache.h /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-05 19:52:40 /// dream: don't forget your dreams! /// ====================================== #ifndef __cache_h__ #define __cache_h__ #include <unordered_map> #include <string> using namespace std; namespace wd { class cache { public: void addelement(string,string);//增加缓存信息 void readfromfile(string);//从文件中读取信息 void writetofile(string);//将信息写入文件中 void update(const cache&);//更新缓存消息 bool find(string querry);//从数据库中找寻信息 string &operator[](string key); private: unordered_map<string,string>_hashtable;//采用hashtable进行缓存 }; }; #endif
///======================================= /// file: cache.cc /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-05 20:01:25 /// dream: don't forget your dreams! /// ====================================== #include "cache.h" #include <fstream> #include <utility> #include <iostream> using namespace std; using namespace wd; void cache::addelement(string querry,string result) { _hashtable[querry]=result; } void cache::readfromfile(string filepath) { ifstream ifs(filepath); if(!ifs){ cout<<"cache::read readfromfile file open error!"<<endl; return; } string querry,result; while(ifs>>querry,!ifs.eof()) { ifs>>result; _hashtable[querry]=result; } } #if 0 void cache::writetofile(string filepath) { ofstream ofs(filepath); if(!ofs){ cout<<""<<endl; return; } for(auto &mypair:_hashtable) { ofs<<mypair.first<<" "; ofs<<mypair.second<<endl; } } void cache::update(const cache & cache) { for(auto &mypair:cache._hashtable) { auto cit =_hashtable.find(mypair.first); if(cit==_hashtable.end()) { _hashtable.insert(std::move(mypair)); } } } #endif void cache::writetofile(string filepath) { ofstream ofs(filepath); if(!ofs){ cout<<"file write error!"<<endl; return; } for(auto &mypair:_hashtable) { ofs<<mypair.first<<" "; ofs<<mypair.second<<endl; } } void cache::update(const cache & cache) { for(auto &mypair:cache._hashtable) { auto cit =_hashtable.find(mypair.first); if(cit==_hashtable.end()) { _hashtable.insert(std::move(mypair)); } } } bool cache::find(string querry) { auto cit =_hashtable.find(querry); if(cit==_hashtable.end()) return false; return true; } string &cache::operator[](string key) { return _hashtable[key]; } #if 0 int main() { cout<<"cache is correct!"<<endl; } #endif
==cachemanger:==
///======================================= /// file: cachemanger.h /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-05 20:51:09 /// dream: don't forget your dreams! /// ====================================== #ifndef __cachemanger_h__ #define __cachemanger_h__ #include "cache.h" #include <vector> #define threadnum 4//线程数目设置为4个,可自定义 using std::vector; namespace wd { class cachemanger { public: cachemanger(string filepath); void init(string);//创建缓存 cache & getcache(size_t);//获取某个缓存 void periodicupdate();//定时更新所有缓存 private: string _cachefilepath; vector<cache>_cachelist; }; }; #endif
///======================================= /// file: cachemanger.cc /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-05 20:56:50 /// dream: don't forget your dreams! /// ====================================== #include "cachemanger.h" #include <iostream> #include <fstream> #include <utility> #include <iostream> using namespace std; using namespace wd; cachemanger::cachemanger(string cachefilepath) { init(cachefilepath); } void cachemanger::init(string cachefilepath) { _cachefilepath=cachefilepath; _cachelist.reserve(threadnum); cache tmp; tmp.readfromfile(_cachefilepath); for(size_t i=0;i!=threadnum;++i) { _cachelist.push_back(std::move(tmp)); } } cache & cachemanger::getcache(size_t number) { return _cachelist[number]; } void cachemanger::periodicupdate() { auto cit=_cachelist.begin(); cache lastwrite=*(cit ++); for(;cit<_cachelist.end();++cit) { lastwrite.update(*cit); } for(cit=_cachelist.begin()+1;cit!=_cachelist.end();++cit) { (*cit).update(lastwrite); } lastwrite.writetofile(_cachefilepath); }
==epollpoller:==
///======================================= /// file: epollpoller.h /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-06 11:03:36 /// dream: don't forget your dreams! /// ====================================== #ifndef __wd_epollpoller_h__ #define __wd_epollpoller_h__ #include "tcpconnection.h" #include "noncopyable.h" #include "mutexlock.h" #include <sys/epoll.h> #include <vector> #include <map> #include <functional> namespace wd { class acceptor; class epollpoller :noncopyable { public: typedef tcpconnection::tcpconnectioncallback epollcallback; typedef std::function<void()> functor; epollpoller(acceptor &acceptor); ~epollpoller(); void loop(); void unloop(); void runinloop(const functor && cb); void dopendingfunctors(); void wakeup(); void setconnectioncallback(epollcallback cb); void setmessagecallback(epollcallback cb); void setclosecallback(epollcallback cb); private: void waitepollfd(); void handleconnection(); void handlemessage(int peerfd); void handleread(); acceptor & _acceptor; int _epollfd; int _eventfd; int _listenfd; bool _islooping; mutexlock _mutex; std::vector<functor> _pendingfunctors; typedef std::vector<struct epoll_event>eventlist; eventlist _eventlist; typedef std::map<int,tcpconnectionptr> connectionmap; connectionmap _connmap; epollcallback _onconnectioncb; epollcallback _onmessagecb; epollcallback _onclosecb; }; } #endif
///======================================= /// file: epollpoller.cc /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-07 15:42:54 /// dream: don't forget your dreams! /// ====================================== #include "epollpoller.h" #include "socketutil.h" #include "acceptor.h" #include <assert.h> #include <iostream> using namespace std; namespace wd { epollpoller::epollpoller(acceptor & acceptor) :_acceptor(acceptor) ,_epollfd(createepollfd()) ,_eventfd(createeventfd()) ,_listenfd(_acceptor.fd()) ,_islooping(false) ,_eventlist(1024) { addepollfdread(_epollfd,_listenfd); addepollfdread(_epollfd,_eventfd); } epollpoller::~epollpoller() { ::close(_epollfd); } void epollpoller::loop() { _islooping=true; while(_islooping) { waitepollfd(); } } void epollpoller::unloop() { if(_islooping) _islooping=false; } void epollpoller::setconnectioncallback(epollcallback cb) { _onconnectioncb=cb; } void epollpoller::setmessagecallback(epollcallback cb) { _onmessagecb=cb; } void epollpoller::setclosecallback(epollcallback cb) { _onclosecb=cb; } void epollpoller::waitepollfd() { int nready; do { nready =::epoll_wait(_epollfd,&(*_eventlist.begin()),_eventlist.size(),10000); }while(-1==nready && errno ==eintr); if(-1==nready){ perror("epoll wait error!"); exit(exit_failure); }else if(0==nready){ cout<<"epoll_wait timeout!"<<endl; }else{//扩容 if(nready==static_cast<int>(_eventlist.size())){ _eventlist.resize(_eventlist.size()*2); } for(int idx=0;idx!=nready;++idx)//正宗罗老师循环体(twt) { if(_eventlist[idx].data.fd ==_listenfd) { if(_eventlist[idx].events & epollin) { handleconnection(); } }else if(_eventlist[idx].data.fd ==_eventfd){ handleread(); cout<<">>dopendingfunctors()"<<endl; dopendingfunctors(); }else{ if(_eventlist[idx].events & epollin){ handlemessage(_eventlist[idx].data.fd); } } } } } void epollpoller::handleconnection() { int peerfd=_acceptor.accept(); addepollfdread(_epollfd,peerfd); tcpconnectionptr conn(new tcpconnection(peerfd,this)); conn->setconnectioncallback(_onconnectioncb); conn->setmessagecallback(_onmessagecb); conn->setclosecallback(_onclosecb); std::pair<connectionmap::iterator,bool>ret; ret=_connmap.insert(std::make_pair(peerfd,conn)); assert(ret.second ==true); (void)ret; conn->handleconnectioncallback(); } void epollpoller::handlemessage(int peerfd) { bool isclosed=isconnectionclosed(peerfd); connectionmap::iterator it =_connmap.find(peerfd); assert(it!=_connmap.end()); if(isclosed) { it->second->handleclosecallback(); delepollreadfd(_epollfd,peerfd); _connmap.erase(it); }else{ it->second->handlemessagecallback(); } } void epollpoller::runinloop(const functor && cb)//在计算线程中执行 { mutexlockguard mlg(_mutex); _pendingfunctors.push_back(std::move(cb)); wakeup(); } void epollpoller::dopendingfunctors() { std::vector<functor>tmp; { mutexlockguard mlg(_mutex); tmp.swap(_pendingfunctors); } for(auto & functor:tmp) { functor(); } } void epollpoller::handleread() { uint64_t howmany; int ret=::read(_eventfd,&howmany,sizeof(howmany)); if(ret !=sizeof(howmany)) { perror("read error!"); } } void epollpoller::wakeup() { uint64_t one =1; int ret =::write(_eventfd,&one,sizeof(one)); if(ret!=sizeof(one)) { perror("write error!"); } } }
==inetaddress:==
///======================================= /// file: inetaddress.h /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-05 21:55:19 /// dream: don't forget your dreams! /// ====================================== #ifndef __wd_inetaddress_h__ #define __wd_inetaddress_h__ #include <netinet/in.h> #include <string> namespace wd { class inetaddress { public: inetaddress(short port); inetaddress(const char *pip,short port); inetaddress(const struct sockaddr_in & addr); std::string ip()const; unsigned short port() const; const struct sockaddr_in *getsockaddrptr() const; private: struct sockaddr_in _addr; }; } #endif
///======================================= /// file: inetaddress.cc /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-07 20:55:18 /// dream: don't forget your dreams! /// ====================================== #include "inetaddress.h" #include <stdio.h> #include <stdlib.h> #include <sys/socket.h> #include <arpa/inet.h> #include <string.h> namespace wd { inetaddress::inetaddress(short port) { ::memset(&_addr,0,sizeof(_addr)); _addr.sin_family=af_inet; _addr.sin_port=htons(port); _addr.sin_addr.s_addr=inaddr_any; } inetaddress::inetaddress(const char * pip,short port) { ::memset(&_addr,0,sizeof(_addr)); _addr.sin_family=af_inet; _addr.sin_port=htons(port); _addr.sin_addr.s_addr=inet_addr(pip); } inetaddress::inetaddress(const struct sockaddr_in & addr) :_addr(addr) {} const struct sockaddr_in * inetaddress::getsockaddrptr()const { return & _addr; } std::string inetaddress::ip()const { return std::string(inet_ntoa(_addr.sin_addr)); } unsigned short inetaddress::port() const { return ntohs(_addr.sin_port); } }
==mutexlock:==
#ifndef __wd_mutexlock_h__ #define __wd_mutexlock_h__ #include "noncopyable.h" #include <pthread.h> namespace wd { class mutexlock :noncopyable { public: mutexlock() {pthread_mutex_init(&_mutex,null);} ~mutexlock() {pthread_mutex_destroy(&_mutex);} void lock() {pthread_mutex_lock(&_mutex);} void unlock() {pthread_mutex_unlock(&_mutex);} pthread_mutex_t *getmutexlockptr() {return &_mutex;} private: pthread_mutex_t _mutex; }; class mutexlockguard//c++之父bs提出的raii { public: mutexlockguard(mutexlock &mutex) :_mutex(mutex) { _mutex.lock(); } ~mutexlockguard() { _mutex.unlock(); } private: mutexlock &_mutex; }; } #endif
==mydict:==
///======================================= /// file: mydict.h /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-06 11:12:19 /// dream: don't forget your dreams! /// ====================================== #ifndef __mydict_h__ #define __mydict_h__ #include <string> #include <vector> #include <map> #include <utility> #include <set> #include <fstream> #include <iostream> #include <sstream> using namespace std; namespace wd { struct myresult { string _word; int _ifreq;//词频 int _idist;//最小编辑距离 }; class mydict { public: mydict(const string dictdir,const string indexdir) { ifstream ifs1(dictdir),ifs2(indexdir); if(!ifs1||!ifs2) cout<<"mydict open file error!"<<endl; string key; int value; ifs1>>value; _dict.push_back(std::make_pair(string(" "),value)); ifs1>>value; _dict.push_back(std::make_pair(string(" "),value)); while(ifs1>>key) { ifs1>>value; _dict.push_back(std::make_pair(key,value)); } string line; while(std::getline(ifs2,line)) { istringstream iss(line); string ikey; int ivalue; iss>>ikey; set<int> tmp; while(iss>>ivalue) { tmp.insert(ivalue); } _index.insert(std::make_pair(ikey,tmp)); } } vector<pair<string,int>> & getdict(){return _dict;} map<string ,set<int>> & getindextable(){return _index;} private: vector<pair<string,int>> _dict; map<string,set<int>> _index; }; } #endif
==mytask:==
///======================================= /// file: mytask.h /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-06 21:04:54 /// dream: don't forget your dreams! /// ====================================== #ifndef __mytask_h__ #define __mytask_h__ #include "tcpconnection.h" #include "configuration.h" #include "mydict.h" #include "cache.h" #include <string> #include <queue> using namespace std; class mycompare { public: bool operator()(const wd::myresult & lhs,const wd::myresult &rhs) { if(lhs._idist !=rhs._idist) return lhs._idist<rhs._idist; else return lhs._ifreq>rhs._ifreq; } private: }; using character =string; class mytask { public: mytask(const string &querry,const wd::tcpconnectionptr conn) :_querry(std::move(querry)) ,_conn(conn) {} void execute(); private: void queryindextable();//查询索引(四个索引) void statistic(set<int> &iset);//计算 int distance(const string & rhs);//计算最小编辑距离 bool response(wd::cache & cache);//响应客户端的请求 vector<character>getonecharacter(const string &word);//获取字符数组 string _querry; wd::tcpconnectionptr _conn; priority_queue<wd::myresult,vector<wd::myresult>,mycompare> _resultque; }; #endif
///======================================= /// file: mytask.cc /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-07 22:47:19 /// dream: don't forget your dreams! /// ====================================== #include "mytask.h" #include "configuration.h" #include "mydict.h" #include "cachemanger.h" #include "json/json.h" #include <string.h> #include <algorithm> extern __thread int t_number; bool mytask::response(wd::cache &cache) { if(cache.find(_querry)) { _conn->sendinloop(cache[_querry]); return true; } return false; } int mytask::distance(const string &rhs) { vector<character>querrycharacter =getonecharacter(_querry); vector<character>indexcharacter =getonecharacter(rhs); int len1,len2; len1=querrycharacter.size(); len2=indexcharacter.size(); int edit[len1+1][len2+1]; int i,j; for(i=0;i<=len1;++i){ for(j=0;j<=len2;++j){ edit[i][j]=0; } } for(i=0;i<len1;++i){ edit[i][0]=i; } for(j=0;j<=len2;++j){ edit[0][j]=j; } for(i=1;i<len1;++i){ for(j=1;j<=len2;++j){ int cost =((querrycharacter[i-1]==indexcharacter[j-1])?0:1); int deletion =edit[i-1][j]+1; int insertion=edit[i][j-1]+1; int substitution=edit[i-1][j-1]+cost; edit[i][j]=std::min(deletion,std::min(insertion,substitution)); } } return edit[len1][len2]; } void mytask::statistic(set<int> &iset) { vector<pair<string,int>>dict=(singleton<wd::mydict>::getinstance(singleton<wd::configuration>::getinstance(confpath)->getdictdir(), singleton<wd::configuration>::getinstance(confpath)->getindexdir()))->getdict(); for(auto &idx:iset) { string key=dict[idx].first; int idist =distance(key); if(idist<=3) { wd::myresult res; res._word=key; res._idist=idist; res._ifreq=dict[idx].second; _resultque.push(res); } } } vector<character>mytask::getonecharacter(const string & word) { auto cit =word.begin(); vector<character> ret; while(cit<word.end()) { if(224==(*cit &224)){ character onecharacter; onecharacter.append(cit,cit+3); ret.push_back(onecharacter); cit =cit+3; }else if(240==(*cit &240)){ character onecharacter; onecharacter.append(cit,cit+4); ret.push_back(onecharacter); cit =cit+4; }else{ character onecharacter(1,*cit); ret.push_back(onecharacter); cit ++; } } return ret; } void mytask::queryindextable() { map<string,set<int>>index=(singleton<wd::mydict>::getinstance(singleton<wd::configuration>::getinstance(confpath)->getdictdir(), singleton<wd::configuration>::getinstance(confpath)->getindexdir()))->getindextable(); vector<character> onecharacter=getonecharacter(_querry); set<int>allrally; for(auto mycharacter:onecharacter) { auto cit =index.find(mycharacter); if(cit!=index.end()) { for(auto &idx:cit->second) allrally.insert(idx); } } statistic(allrally); } void mytask::execute() { wd::cache &mycache =(singleton<wd::cachemanger>::getinstance(singleton<wd::configuration>::getinstance(confpath)->getcache()))->getcache(t_number); if(response(mycache)) return; else{ queryindextable(); json::fastwriter writerinfo; json::value arrayobj; while(!_resultque.empty()) { json::value new_item; new_item[""]=_resultque.top()._word; _resultque.pop(); arrayobj.append(new_item); } string stremail=writerinfo.write(arrayobj); mycache.addelement(_querry,stremail); _conn->sendinloop(stremail); } }
==noncopyable:==
#ifndef __wd_noncopyable_h__ #define __wd_noncopyable_h__ namespace wd { class noncopyable { protected: noncopyable(){} ~noncopyable(){} private: noncopyable(const noncopyable &); noncopyable & operator=(const noncopyable &); }; } #endif
==socket:==
///======================================= /// file: socket.h /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-05 21:46:26 /// dream: don't forget your dreams! /// ====================================== #ifndef __wd_socket_h__ #define __wd_socket_h__ #include "noncopyable.h" namespace wd { class inetaddress; class socket :noncopyable { public: socket(int socket); socket(); ~socket(); void shutdownwrite(); int fd()const {return _sockfd;} void nonblock(); static inetaddress getlocaladdr(int socketfd); static inetaddress getpeeraddr(int sockfd); private: int _sockfd; }; } #endif
///======================================= /// file: socket.cc /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-07 20:38:20 /// dream: don't forget your dreams! /// ====================================== #include "socket.h" #include "socketutil.h" #include "inetaddress.h" namespace wd { socket::socket(int sockfd) :_sockfd(sockfd) {} socket::socket() :_sockfd(createsocketfd()) {} socket::~socket() { ::close(_sockfd); } void socket::nonblock() { setnonblock(_sockfd); } void socket::shutdownwrite() { if(-1==::shutdown(_sockfd,shut_wr)){ perror("shutdown write error!"); } } inetaddress socket::getlocaladdr(int sockfd) { struct sockaddr_in addr; socklen_t len=sizeof(sockaddr_in); if(-1==::getsockname(sockfd,(struct sockaddr *)&addr,&len)){ perror("getsockname error!"); } return inetaddress(addr); } inetaddress socket::getpeeraddr(int sockfd) { struct sockaddr_in addr; socklen_t len=sizeof(sockaddr_in); if(-1==::getpeername(sockfd,(struct sockaddr *)&addr,&len)){ perror("getpeername error!"); } return inetaddress(addr); } }
==socketio:==
///======================================= /// file: socketio.h /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-06 17:10:23 /// dream: don't forget your dreams! /// ====================================== #ifndef __socketio_h__ #define __socketio_h__ #include <stdio.h> namespace wd { class socketio { public: socketio(int sockfd); size_t readn(char *buf,size_t count); size_t writen(const char *buf,size_t count); size_t readline(char *buf,size_t max_len); private: size_t recv_peek(char *buf,size_t count); int _sockfd; }; } #endif
///======================================= /// file: socketio.cc /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-07 21:56:34 /// dream: don't forget your dreams! /// ====================================== #include "socketio.h" #include "socketutil.h" namespace wd { socketio::socketio(int sockfd) :_sockfd(sockfd) {} size_t socketio::readn(char *buf,size_t count) { size_t nleft =count; char *pbuf=buf; while(nleft>0) { int nread =::read(_sockfd,pbuf,nleft); if(-1==nread) { if(errno ==eintr) continue; return exit_failure; }else if(0==nread){ break; } pbuf =pbuf+nread; nleft=nleft-nread; } return (count -nleft); } size_t socketio::writen(const char * buf,size_t count) { size_t nleft =count; const char *pbuf=buf; while(nleft >0) { int nwrite=::write(_sockfd,pbuf,nleft); if(-1==nwrite) { if(errno ==eintr) continue; return exit_failure; } nleft =nleft -nwrite; pbuf =pbuf +nwrite; } return (count -nleft); } size_t socketio::recv_peek(char *buf,size_t count) { int nread; do{ nread=::recv(_sockfd,buf,count,msg_peek); }while(-1==nread && errno ==eintr); return nread; } size_t socketio::readline(char *buf,size_t maxlen) { size_t nleft =maxlen-1; char *pbuf=buf; size_t total=0; while(nleft>0) { size_t nread =recv_peek(pbuf,nleft); if(nread<=0) return nread; for(size_t idx =0;idx!=nread;++idx){//检查换行符/n if(pbuf[idx]=='\n'){ size_t nsize =idx +1; if(readn(pbuf,nsize)!=nsize) return exit_failure; pbuf +=nsize; total +=nsize; *pbuf=0; return total; } } if(readn(pbuf,nread)!=nread) return exit_failure; pbuf +=nread; nleft -=nread; total +=nread; } *pbuf=0; return maxlen-1; } }
==sockutil(根据陈硕编写的linux书上分开头文件)==
///======================================= /// file: socktutil.h /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-05 22:01:30 /// dream: don't forget your dreams! /// ====================================== #ifndef __wd_sockerutil_h__ #define __wd_sockerutil_h__ #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/eventfd.h> #include <sys/epoll.h> namespace wd { inline int createsocketfd() { int fd=::socket(af_inet,sock_stream,0); if(-1==fd) { perror("socket create error!"); } return fd; } inline void setnonblock(int fd) { int flags=::fcntl(fd,f_getfl,0); flags |= o_nonblock; ::fcntl(fd,f_setfl,flags); } inline int createepollfd() { int efd=::epoll_create1(0); if(-1==efd) { perror("epoll create1 error!"); exit(exit_failure); } return efd; } inline int createeventfd() { int evtfd=::eventfd(0,efd_nonblock|efd_cloexec); if(-1==evtfd) { perror("eventfd create error!"); } return evtfd; } inline void addepollfdread(int efd,int fd) { struct epoll_event ev; ev.data.fd=fd; ev.events=epollin; int ret=epoll_ctl(efd,epoll_ctl_add,fd,&ev); if(-1==ret) { perror("epoll ctl add error!"); exit(exit_failure); } } inline void delepollreadfd(int efd,int fd) { struct epoll_event ev; ev.data.fd=fd; int ret=epoll_ctl(efd,epoll_ctl_del,fd,&ev); if(-1==ret) { perror("epoll ctl delete error!"); exit(exit_failure); } } inline size_t recvpeek(int sockfd,void *buf,size_t len) { int nread; do{ nread=::recv(sockfd,buf,len,msg_peek); }while(nread==-1 && errno ==eintr); return nread; } inline bool isconnectionclosed(int sockfd) { char buf[1024]; int nread=recvpeek(sockfd,buf,sizeof(buf)); if(-1==nread) { perror("recvpeek error!"); return true; } return (0==nread); } } #endif
==spellcorrrectsever:==
///======================================= /// file: spellcorrectserver.h /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-06 20:41:13 /// dream: don't forget your dreams! /// ====================================== #ifndef __spellcorrectserver_h__ #define __spellcorrectserver_h__ #include "tcpserver.h" #include "threadpool.h" using namespace wd; namespace wd { class spellcorrectserver { public: spellcorrectserver(const string & ip ,unsigned short port ,size_t threadnum ,size_t quesize); void start(); private: void onconnection(const tcpconnectionptr &); void onmessage(const tcpconnectionptr &); void onclose(const tcpconnectionptr &); tcpserver _tcpserver; threadpool _threadpoll; }; }; #endif
///======================================= /// file: spellcorrectserver.cc /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-07 21:20:41 /// dream: don't forget your dreams! /// ====================================== #include "spellcorrectserver.h" #include "mytask.h" #include <stdio.h> #include <iostream> #include <string> #include <utility> #include <functional> using namespace std; void spellcorrectserver::onconnection(const wd::tcpconnectionptr & conn) { cout<<conn->tostring()<<endl; conn->send("hello ,welcome to wtp chat server.\r\n"); } void spellcorrectserver::onmessage(const wd::tcpconnectionptr & conn) { string s(conn->receive()); mytask task(s,conn); _threadpoll.addtask(std::bind(&mytask::execute,&task)); cout<<">add task to threadpool"<<endl; } void spellcorrectserver::onclose(const wd::tcpconnectionptr &conn) { ::printf("%s close\n",conn->tostring().c_str()); } spellcorrectserver::spellcorrectserver(const string & ip ,unsigned short port ,size_t threadnum ,size_t quesize) :_tcpserver(ip,port) ,_threadpoll(threadnum,quesize) {} void spellcorrectserver::start() { _threadpoll.start(); _tcpserver.setconnectioncallback(std::bind(&spellcorrectserver::onconnection,this,std::placeholders::_1)); _tcpserver.setmessagecallback(std::bind(&spellcorrectserver::onmessage,this,std::placeholders::_1)); _tcpserver.setclosecallback(std::bind(&spellcorrectserver::onclose,this,std::placeholders::_1)); _tcpserver.start(); }
==taskque:==
#ifndef __wd_taskqueue_h__ #define __wd_taskqueue_h__ #include "mutexlock.h" #include "condition.h" #include <queue> #include <functional> namespace wd { typedef std::function<void()>task; class taskqueue { public: taskqueue(size_t quesize) :_quesize(quesize) ,_mutex() ,_notfull(_mutex) ,_notempty(_mutex) ,_flag(true) {} void push(task &&task); task pop(); bool empty()const { return _que.size()==0; } bool full()const {return _que.size()==_quesize;} void wakeup() { if(_flag) _flag=false; _notempty.notifyall(); } private: size_t _quesize; std::queue<task> _que; mutexlock _mutex; condition _notfull; condition _notempty; bool _flag; }; } #endif
#include "taskqueue.h" using namespace wd; //生产者所在的线程 void taskqueue::push(task && task) { mutexlockguard autolock(_mutex); while(full()) { _notfull.wait(); } _que.push(std::move(task)); _notempty.notify(); } //消费者所在线程 task taskqueue::pop() { mutexlockguard autolock(_mutex); while(_flag && empty()) { _notempty.wait(); } if(_flag){ task task=_que.front(); _que.pop(); _notfull.notify(); return task; }else{ return null; } } #if 0 task taskqueue::pop() { mutexlockguard autolock(_mutex); while(_flag && empty()) { _notempty.wait(); } if(_flag){ task task =_que.front(); _que.pop(); _notfull.notify(); return task; }else{ return null; } } #endif
==tcpconnection:==
///======================================= /// file: tcpconnection.h /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-06 17:15:33 /// dream: don't forget your dreams! /// ====================================== #ifndef __wd_tcpconnection_h__ #define __wd_tcpconnection_h__ #include "noncopyable.h" #include "inetaddress.h" #include "socket.h" #include "socketio.h" #include <string> #include <memory> #include <functional> namespace wd { class epollpoller; class tcpconnection; typedef std::shared_ptr<tcpconnection> tcpconnectionptr; class tcpconnection :noncopyable ,public std::enable_shared_from_this<tcpconnection> { public: typedef std::function<void(const tcpconnectionptr &)>tcpconnectioncallback; tcpconnection(int sockfd,epollpoller *loop); ~tcpconnection(); std::string receive(); void send(const std::string &msg); void sendinloop(const std::string &msg); void shutdown(); std::string tostring(); void setconnectioncallback(tcpconnectioncallback cb); void setmessagecallback(tcpconnectioncallback cb); void setclosecallback(tcpconnectioncallback cb); void handleconnectioncallback(); void handlemessagecallback(); void handleclosecallback(); private: socket _sockfd; socketio _sockio; const inetaddress _localaddr; const inetaddress _peeraddr; bool _isshutdownwrite; epollpoller * _loop; tcpconnectioncallback _onconnectioncb; tcpconnectioncallback _onmessagecb; tcpconnectioncallback _onclosecb; }; } #endif
///======================================= /// file: tcpconnection.cc /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-07 22:22:22 /// dream: don't forget your dreams! /// ====================================== #include "tcpconnection.h" #include "epollpoller.h" #include <string.h> #include <stdio.h> namespace wd { tcpconnection::tcpconnection(int sockfd,epollpoller * loop) :_sockfd(sockfd) ,_sockio(sockfd) ,_localaddr(wd::socket::getlocaladdr(sockfd)) ,_peeraddr(wd::socket::getpeeraddr(sockfd)) ,_isshutdownwrite(false) ,_loop(loop) {_sockfd.nonblock();} tcpconnection::~tcpconnection() { if(!_isshutdownwrite) { _isshutdownwrite=true; shutdown(); } printf("~tcpconnection()\n"); } std::string tcpconnection::receive() { char buf[65536]; memset(buf,0,sizeof(buf)); size_t ret =_sockio.readline(buf,sizeof(buf)); if(0==ret){ return std::string(); }else{ return std::string(buf); } } void tcpconnection::send(const std::string &msg) { size_t len=msg.size(); _sockio.writen((const char *)&len,sizeof(int)); _sockio.writen(msg.c_str(),len); } void tcpconnection::shutdown() { if(!_isshutdownwrite) _sockfd.shutdownwrite(); _isshutdownwrite=true; } std::string tcpconnection::tostring() { char str[100]; snprintf(str,sizeof(str),"%s:%d->%s:%d" ,_localaddr.ip().c_str() ,_localaddr.port() ,_peeraddr.ip().c_str() ,_peeraddr.port()); return std::string(str); } void tcpconnection::setconnectioncallback(tcpconnectioncallback cb) { _onconnectioncb =cb; } void tcpconnection::setmessagecallback(tcpconnectioncallback cb) { _onmessagecb =cb; } void tcpconnection::setclosecallback(tcpconnectioncallback cb) { _onclosecb =cb; } void tcpconnection::handleconnectioncallback() { if(_onconnectioncb){ _onconnectioncb(shared_from_this()); } } void tcpconnection::handlemessagecallback() { if(_onmessagecb){ _onmessagecb(shared_from_this()); } } void tcpconnection::handleclosecallback() { if(_onclosecb){ _onclosecb(shared_from_this()); } } void tcpconnection::sendinloop(const std::string & msg) { _loop->runinloop(std::bind(&tcpconnection::send,this,msg)); } }
==tcpserver:==
///======================================= /// file: tcpserver.h /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-06 20:15:21 /// dream: don't forget your dreams! /// ====================================== #ifndef __wd_tcpserver_h__ #define __wd_tcpserver_h__ #include "acceptor.h" #include "epollpoller.h" #include <string> using std::string; namespace wd { class tcpserver { public: typedef epollpoller::epollcallback tcpservercallback; tcpserver(const string & ip,unsigned short port); tcpserver(unsigned short port); void start(); void stop(); void setconnectioncallback(tcpservercallback cb); void setmessagecallback(tcpservercallback cb); void setclosecallback(tcpservercallback cb); private: acceptor _acceptor; epollpoller _poller; tcpservercallback _connectioncallback; tcpservercallback _messagecallback; tcpservercallback _closecallback; }; } #endif
///======================================= /// file: tcpserver.cc /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-07 19:59:37 /// dream: don't forget your dreams! /// ====================================== #include "tcpserver.h" #include "inetaddress.h" #include "socketutil.h" #include <iostream> using namespace std; namespace wd { tcpserver::tcpserver(const string & ip,unsigned short port) :_acceptor(createsocketfd(),inetaddress(ip.c_str(),port)) ,_poller(_acceptor) {} void tcpserver::start() { _acceptor.ready(); _poller.setconnectioncallback(_connectioncallback); _poller.setmessagecallback(_messagecallback); _poller.setclosecallback(_closecallback); _poller.loop(); } void tcpserver::stop() { _poller.unloop(); } void tcpserver::setconnectioncallback(tcpservercallback cb) {_connectioncallback=cb;} void tcpserver::setmessagecallback(tcpservercallback cb) {_messagecallback=cb;} void tcpserver::setclosecallback(tcpservercallback cb) {_closecallback=cb;} }
==thread:==
#ifndef __wd_thread_h__ #define __wd_thread_h__ #include "noncopyable.h" #include <pthread.h> #include <functional> using std::function; namespace wd { class thread; struct threadptr { int _number; thread *_pthread; }; class thread :noncopyable { using threadcallback =function<void()>; public: thread(threadcallback &&cb); ~thread(); void start(int number); void join(); bool isrunning()const {return _isrunning;} private: static void * threadfunc(void *); pthread_t _pthid; bool _isrunning; threadcallback _cb; }; } #endif
#include "thread.h" #include <iostream> using namespace std; using namespace wd; __thread int t_number;//将线程编号作为线程存储的标记 thread::thread(threadcallback && cb)//这里的右值引用本身取决于是否有名字 :_pthid(0) ,_isrunning(false) ,_cb(std::move(cb)) { cout<<"thread(cb)"<<endl; } void thread::start(int number) { threadptr *threadptr=new threadptr(); threadptr->_number=number; threadptr->_pthread=this; pthread_create(&_pthid,null,threadfunc,threadptr); _isrunning=true; } void *thread::threadfunc(void *arg) {//应用了线程存储 threadptr *threadptr=static_cast<threadptr*>(arg); thread * pthread=threadptr->_pthread; t_number=threadptr->_number; if(pthread) pthread->_cb();//线程开始工作! delete threadptr; //thread * pthread =threadptr->_pthread; return null; } #if 0 void *thread::threadfunc(void *arg) { threadptr *threadptr =static_cast<threadptr*>(arg); thread * pthread =threadptr->_pthread; t_number =threadptr->_number; if(pthread) pthread->_cb(); delete threadptr; return null; } #endif void thread::join() { pthread_join(_pthid,null); _isrunning=false; } thread::~thread() { if(_isrunning) { pthread_detach(_pthid); _isrunning=false; } cout<<"~thread()"<<endl; }
==threadpool:==
#ifndef __wd_threadpoll_h__ #define __wd_threadpoll_h__ #include "taskqueue.h" #include "thread.h" #include <vector> #include <memory> #include <functional> using std::shared_ptr; using std::vector; namespace wd { class threadpool { public: using task=std::function<void()>; threadpool(size_t threadnum,size_t quesize) :_threadnum(threadnum) ,_quesize(quesize) ,_taskque(_quesize) ,_isexit(false) { _threads.reserve(_threadnum); } ~threadpool(); void start(); void stop(); void addtask(task && task); private: void threadfunc(); task gettask(); size_t _threadnum; size_t _quesize; vector<shared_ptr<thread>> _threads; taskqueue _taskque; bool _isexit; }; } #endif
#include "threadpool.h" #include "thread.h" #include <unistd.h> #include <iostream> using namespace std; using namespace wd; void threadpool::start() { for(size_t idx=0;idx<_threadnum;++idx) { shared_ptr<thread>pthread(new thread(std::bind(&threadpool::threadfunc,this))); _threads.push_back(std::move(pthread)); } int number=0; for(auto &pthread:_threads) { pthread->start(number); ++number; } } void threadpool::stop()//为了线程安全,将stop方法置于主线程中 { if(!_isexit) { while(!_taskque.empty()){ ::sleep(1); cout<<"threadpool sleep 1 second!"<<endl; } _isexit=true; cout<<"threadpool ->stop:_isexit="<<_isexit<<endl; _taskque.wakeup(); for(auto &pthread:_threads){ pthread->join(); } } } threadpool::~threadpool() { if(!_isexit){ stop(); } } void threadpool::addtask(task && task) { _taskque.push(std::move(task)); } task threadpool::gettask() { return _taskque.pop(); } void threadpool::threadfunc() { while(!_isexit) { task task=gettask(); if(task){ task(); } } }
==timer:==
///======================================= /// file: timer.h /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-06 20:00:45 /// dream: don't forget your dreams! /// ====================================== #ifndef __wd_timer_h__ #define __wd_timer_h__ #include <functional> namespace wd { class timer { public: using timercallback =std::function<void()>; timer(int initailtime,int intervaltime,timercallback && cb); ~timer(); void start(); void stop(); private: int _fd; int _initialtime; int _intervaltime; timercallback _cb; bool _isstarted; int createtimerfd(); void settimerfd(int initialtime, int intervaltime); void handleread(); }; } #endif
///======================================= /// file: timer.cc /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-07 20:09:14 /// dream: don't forget your dreams! /// ====================================== #include "timer.h" #include <unistd.h> #include <errno.h> #include <poll.h> #include <sys/timerfd.h> #include <iostream> using namespace std; using namespace wd; timer::timer(int initialtime,int intervaltime,timercallback && cb) :_fd(createtimerfd()) ,_initialtime(initialtime) ,_intervaltime(intervaltime) ,_cb(std::move(cb)) ,_isstarted(false) {} void timer::start() { struct pollfd pfd; pfd.fd=_fd; pfd.events=pollin; settimerfd(_initialtime,_intervaltime);//开启定时器 _isstarted=true; while(_isstarted){ int nready=::poll(&pfd,1,5000); if(-1==nready &&errno ==eintr){ continue; }else if(-1==nready){ perror(">>>poll error!"); exit(exit_failure); }else if(0==nready){ cout<<">>>poll timeout!"<<endl; }else{ if(pfd.revents & pollin){ handleread();//先对定时器进行处理 if(_cb){ _cb();//再去执行回调任务 } } } } } void timer::stop() { settimerfd(0,0); if(_isstarted){ _isstarted=false; } } timer::~timer() { if(_isstarted){ stop(); } } int timer::createtimerfd() { int fd=::timerfd_create(clock_realtime,0); if(-1==fd){ perror(">>timerfd_create error!"); } return fd; } void timer::settimerfd(int initialtime,int intervaltime) { struct itimerspec value; value.it_value.tv_sec=initialtime; value.it_value.tv_nsec=0; value.it_interval.tv_sec=intervaltime; value.it_interval.tv_nsec=0; int ret=::timerfd_settime(_fd,0,&value,null); if(-1==ret){ perror(">>>timerfd_settime error!"); } } #if 0 void timer::handleread() { uint64_t howmany; int ret =::read(_fd,&howmany,sizeof(uint64_t)); if(ret!=sizeof(uint64_t)){ perror("read!"); } } #endif void timer::handleread() { uint64_t howmany;//为一个64位 int ret=::read(_fd,&howmany,sizeof(uint64_t)); if(ret!=sizeof(uint64_t)){ perror(">>>read error!"); } }
==timerthread:==
///======================================= /// file: timerthread.cc /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-07 17:12:51 /// dream: don't forget your dreams! /// ====================================== #ifndef __wd_timerthread_h__ #define __wd_timerthread_h__ #include "timer.h" #include "thread.h" #include <functional> namespace wd { class timerthread { public: using timercallback = std::function<void()>; timerthread(int, int, timercallback && cb); ~timerthread(); void start(); void stop(); private: timer _timer; thread _subthread; bool _isstarted; }; }//end of namespace wd #endif
///======================================= /// file: timerthread.cc /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-07 20:09:14 /// dream: don't forget your dreams! /// ====================================== #include "timerthread.h" using namespace wd; timerthread::timerthread(int initialtime, int intervaltime, timercallback && cb) : _timer(initialtime, intervaltime, std::move(cb)) , _subthread(std::bind(&timer::start, &_timer)) , _isstarted(false) {} void timerthread::start() { _subthread.start(0); _isstarted = true; } void timerthread::stop() { if(_isstarted) { _timer.stop(); _subthread.join(); _isstarted = false; } } timerthread::~timerthread() { if(_isstarted) stop(); }
==main:==
///======================================= /// file: main.cc /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-07 21:09:32 /// dream: don't forget your dreams! /// ====================================== #include "spellcorrectserver.h" #include "configuration.h" #include "cachemanger.h" #include "timerthread.h" #include <iostream> #include <functional> using namespace std; using namespace wd; int main() { wd::cachemanger *mycachemanger=singleton<cachemanger>::getinstance(singleton<configuration> ::getinstance(confpath)->getcache()); timerthread timer(5,600,std::bind(&cachemanger::periodicupdate,mycachemanger)); timer.start(); spellcorrectserver myspell(singleton<configuration>::getinstance(confpath)->getip() ,singleton<configuration>::getinstance(confpath)->getport() ,4 ,10); myspell.start(); return 0; }
小结:(1)已经实现项目需求,中文和英文单词都能查询,经过测试,运行稳定,能输出不少候选词
(2)仍然存在少量bug,例如偶尔会发生段错误
(3)由于时间问题,json读出的数据key-value的key值没有打印,用户界面还未来得及优化
(4)陈硕的《linux多线程服务端编程》使用linux接口(timerfd),没用posix接口(eventfd)
---恢复内容结束---
# 我的spellcorrect 开发文档
[toc]
相关配置文件及准备工作:
我的主要文件夹分为三个:分别为客户端,cppjieba分词库,离线部分及服务器部分
客户端部分:内部为客户端源码及运行程序
cppjieba分词库部分,就不赘述,请自行安装
离线部分:内部有中英文件夹放置索引及词典文件,还有配置文件及分词库,其余为代码
服务器部分:最为重要的配置文件及数据部分,头文件在include,实现文件在src里面
演示效果(中文):
==启动时服务器:==
==客户端连入时时服务器:==
==输入“赵”==
==输入“周杰伦”==
==输入清华大学==
演示效果(英语):
==启动时服务器:==
==客户端连入时时服务器:==
==输入student:==
==输入spell:==
==输入computer:==
代码部分:
离线部分:
==makefile:==
srcs:=$(wildcard *.cc) objs:= $(patsubst %.cc, %.o, $(srcs)) cxx:=g++ cxxflags:= -w -g -std=c++11 $(addprefix -i, $(inc_dir)) $(libs) -wno-deprecated -i ../cppjieba/include/ -i ../cppjieba/deps exe:=./main $(exe):$(objs) $(cxx) -o $(exe) $(objs) $(cxxflags) clean: rm -rf $(exe) rm -rf $(objs)
==configuration:==
///======================================= /// file: configuration.h /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-12 00:30:39 /// dream: don't forget your dreams! /// ====================================== #ifndef __configuration_h__ #define __configuration_h__ #include "nocopyable.h" #include <string> #include <map> using namespace std; class configuration :public noncopyable { public: configuration(const string &filepath); string getenglishdir() const {return _englishdir;} string getchinesedir() const {return _chinesedir;} private: string _filepath; string _englishdir; string _chinesedir; }; template<typename t> class singleton { public: template<typename ...args> static t* getinstance(args ...args) { if(!_pinstance) _pinstance = new t(args...); return _pinstance; } static void destroy() { if(_pinstance) delete _pinstance; } private: singleton(); ~singleton(); static t *_pinstance; }; template<typename t> t * singleton<t>::_pinstance = null; #endif
1 ///======================================= 2 /// file: configuration.cc 3 /// author: wtptorres(1584292712@qq.com) 4 /// date: 2019-06-12 00:30:04 5 /// dream: don't forget your dreams! 6 /// ====================================== 7 8 9 #include "configuration.h" 10 #include <utility> 11 #include <fstream> 12 #include <iostream> 13 using namespace std; 14 15 configuration::configuration(const string &filepath) 16 :_filepath(std::move(filepath)) 17 { 18 ifstream ifs(_filepath); 19 if(!ifs) 20 cout<<"file open error!"<<endl; 21 ifs>>_englishdir; 22 ifs>>_chinesedir; 23 ifs.close(); 24 }
///======================================= /// file: nocopyable.h /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-12 00:24:36 /// dream: don't forget your dreams! /// ====================================== #ifndef __nocopyable_h__ #define __nocopyable_h__ class noncopyable { public: noncopyable()=default; ~noncopyable()=default; private: noncopyable(const noncopyable &rhs); noncopyable &operator =(const noncopyable &rhs); }; #endif
==dictproducer:==
///======================================= /// file: dictproducer.h /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-12 16:40:25 /// dream: don't forget your dreams! /// ====================================== #ifndef __dictproducer_h__ #define __dictproducer_h__ #include "splittool.h" using namespace std; #include "nocopyable.h" #include <memory> #include <string> #include <vector> #include <map> #include <utility> class dictproducer :public noncopyable { public: dictproducer(const string,const string,const string &,splittooljieba *); ~dictproducer(){} void build_dict(); void build_cn_dict(); void store_dict(); vector<pair<string,int>>& getindict(){return _indict;} private: void processenglishword(string &word); void processchineseword(string &word);//除去中文的数字 void construct_indict(); string _englishdir; string _chinesedir; string _golefilepath; vector<string> _englishfiles; vector<string> _chinesefiles; map<string,int> _dict; vector<pair<string,int>> _indict; shared_ptr<splittooljieba> _splittool; }; #endif
///======================================= /// file: dictproducer.cc /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-12 16:50:46 /// dream: don't forget your dreams! /// ====================================== #include "dictproducer.h" #include <cctype> #include <utility> #include <fstream> #include <iostream> #define firstsize 10000 using namespace std; dictproducer::dictproducer(const string englishdir,const string chinesedir,const string &golefilepath,splittooljieba *splittoolptr) :_englishdir(englishdir) ,_chinesedir(chinesedir) ,_golefilepath(golefilepath) { _splittool.reset(splittoolptr); std::ifstream ifsenglish(_englishdir); std::ifstream ifschinese(_chinesedir); string filepath; if(!ifsenglish || !ifschinese){ cout<<"dict file open error!"<<endl; } while(ifsenglish>>filepath) { _englishfiles.push_back(filepath); } while(ifschinese>>filepath) { _chinesefiles.push_back(filepath); } _indict.reserve(firstsize); } void dictproducer::processenglishword(string &word) { auto cit =word.begin(); for(;cit!=word.end();++cit) { //去除标点符号或数字 if(!isalpha(*cit)){ word.erase(cit); --cit;//迭代器位置发生改变 }else if(isupper(*cit)){//将大写字母改为小写 *cit =tolower(*cit); } } } void dictproducer::processchineseword(string &word) { auto cit =word.begin(); for(;cit!=word.end();++cit) { //去除数字 if(!isalnum(*cit)){ word.erase(cit); --cit; } } } void dictproducer::build_dict()//建立英文词典 { for(auto &filepath:_englishfiles) { ifstream ifs(filepath); if(!ifs){ cout<<"english file open error!"<<endl; } string word; while(ifs>>word) { processenglishword(word); auto cit =_dict.find(word); if(word.size()>0 && cit ==_dict.end()){ _dict.insert(std::make_pair(word,1)); }else if(cit!=_dict.end()){ ++ cit ->second; } } } } void dictproducer::build_cn_dict() { vector<string>words; for(auto filepath:_chinesefiles) { ifstream ifs(filepath); if(!ifs){ cout<<"chinese file open error!"<<endl; } string sentence; while(std::getline(ifs,sentence)) { _splittool->cut(sentence); } } vector<string>& results =_splittool->getresult(); for(auto &res:results) { processchineseword(res); auto cit =_dict.find(res); if(cit ==_dict.end()){ _dict.insert(std::make_pair(res,1)); }else{ ++ cit ->second; } } } void dictproducer::store_dict() { construct_indict(); ofstream ofs(_golefilepath); if(!ofs) cout<<"store_dict open file error!"<<endl; for(auto &mypair:_indict) { ofs<<mypair.first<<" "<<mypair.second<<endl; } ofs.close(); } void dictproducer::construct_indict() { for(auto dictpair:_dict){ _indict.push_back(dictpair); } }
==getindex:==
///======================================= /// file: getindex.h /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-12 08:52:04 /// dream: don't forget your dreams! /// ====================================== #ifndef __getindex_h__ #define __getindex_h__ #include <string> #include <unordered_map> #include <set> #include <vector> #include <utility> #include <unordered_set> using namespace std; class getindex { public: getindex(const string &,const string &,const string &); ~getindex(){} void construct_index(); void store_index(); private: bool isenglish(const string &rhs) const; vector<string>getonecharacter(const string & word); string _sourcefilepath; string _golefilepath; string _stopwordsfilepath; vector<pair<string,int>>_dict; unordered_set<string>_stopwords; unordered_map<string,set<int>>_index; }; #endif
///======================================= /// file: getindex.cc /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-12 09:00:11 /// dream: don't forget your dreams! /// ====================================== #include "getindex.h" #include <fstream> #include <sstream> #include <iostream> using namespace std; getindex::getindex(const string & sourcefilepath,const string &golefilepath,const string &stopwordsfilepath) :_sourcefilepath(std::move(sourcefilepath)) ,_golefilepath(std::move(golefilepath)) ,_stopwordsfilepath(std::move(stopwordsfilepath)) { ifstream ifs(_sourcefilepath); if(!ifs){ cout<<"getindex file open error!"<<endl; } string line; while(getline(ifs,line)) { istringstream iss(line); string key; int value; iss>>key>>value; _dict.push_back(std::make_pair(key,value)); } ifstream ifs1(_stopwordsfilepath); if(!ifs1){ cout<<"file open error!"<<endl; } string stopword; while(ifs1>>stopword,!ifs1.eof()) { _stopwords.insert(stopword); } } vector<string> getindex::getonecharacter(const string &word) { vector<string>tmp; auto cit =word.begin(); while(cit<word.end()) { if(224==(*cit &224)) { string onecharacter; onecharacter.append(cit,cit+3); tmp.push_back(onecharacter); cit +=3; }else if(240==(*cit &240)){ string onecharacter; onecharacter.append(cit,cit+4); tmp.push_back(onecharacter); cit +=4; }else break; } return tmp; } bool getindex::isenglish(const string &rhs) const { char ch =*(rhs.begin()); if(ch<0) return false; return true; } #if 0 bool getindex::isenglish(const string &rhs) const { char ch =*(rhs.begin()); if(ch<0){ return false; } return true; } #endif void getindex::construct_index() { for(size_t i=0;i!=_dict.size();++i) { string tmp=_dict[i].first; if(isenglish(tmp)) { for(auto ch:tmp) { string charactor(1,ch); if(isalpha(ch)) { auto cit =_index.find(charactor); if(cit ==_index.end()) { set<int> smp; smp.insert(i); _index.insert(std::make_pair(charactor,smp)); }else{//已经存在了该字母的索引 cit->second.insert(i); } } } }else{//中文处理部分 vector<string> onecharacterrally =getonecharacter(tmp); for(auto onecharacter:onecharacterrally) {//stop_words中不存在该单词,则加入索引中 if(_stopwords.find(onecharacter)==_stopwords.end()){ auto it =_index.find(onecharacter); if(it == _index.end()){ set<int>tmp; tmp.insert(i); _index.insert(std::make_pair(onecharacter,tmp)); }else{ it->second.insert(i); } } } } } } void getindex::store_index() { //ofs存储索引的内容 std::ofstream ofs(_golefilepath); if(!ofs){ cout<<"file open error!"<<endl; return; } for(auto data:_index) { ofs<<data.first<<" "; for(auto linenum:data.second) { ofs<<linenum<<" "; } ofs<<endl; } ofs.close(); }
==splittool:==
///======================================= /// file: splittool.h /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-12 17:12:01 /// dream: don't forget your dreams! /// ====================================== #ifndef __splittool_h__ #define __splittool_h__ #include "../cppjieba/include/cppjieba/jieba.hpp"//需要自己将cppjieba安装在项目目录下 #include "configuration.h" #include <string> #include <vector> using namespace std; using namespace cppjieba; class splittooljieba { public: splittooljieba(const string& dict_path,const string &model_path,const string &user_dict_path,const string & idfpath, const string &stopwordpath) :_jieba(dict_path,model_path,user_dict_path,idfpath,stopwordpath) {} ~splittooljieba(){} void cut(const string & sentence) { vector<string>tmp; _jieba.cut(sentence,tmp); _result.insert(_result.end(),tmp.begin(),tmp.end()); } vector<string> & getresult(){return _result;} private: vector<string> _result; cppjieba::jieba _jieba; }; #endif
==main:==
///======================================= /// file: main.cc /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-12 20:38:50 /// dream: don't forget your dreams! /// ====================================== #include "configuration.h" #include "splittool.h" using namespace std; #include "dictproducer.h" #include "getindex.h" #include <iostream> #include <memory> const char * const dict_path ="../cppjieba/dict/jieba.dict.utf8"; const char * const hmm_path ="../cppjieba/dict/hmm_model.utf8"; const char * const user_dict_path ="../cppjieba/dict/user.dict.utf8"; const char * const idf_path ="../cppjieba/dict/idf.utf8"; const char * const stop_word_path ="../cppjieba/dict/stop_words.utf8"; const string gole_dict_path="../server/data/dict.txt"; const string gole_index_path="../server/data/index.txt"; class splittool; int main(void) { configuration *pconfig =singleton<configuration>::getinstance("configure.txt"); splittooljieba *ptool =new splittooljieba(dict_path,hmm_path,user_dict_path,idf_path,stop_word_path); dictproducer mydictproducer(pconfig->getenglishdir(),pconfig->getchinesedir(),gole_dict_path,ptool); mydictproducer.build_dict();//建立英语词典 mydictproducer.build_cn_dict();//建立中文词典 mydictproducer.store_dict();//储存词典 getindex myindex(gole_dict_path,gole_index_path,"stop_words_zh.txt"); myindex.construct_index();//建立索引 myindex.store_index();//存储索引 singleton<configuration>::destroy(); return 0; }
在线部分:
==configuration:==
///======================================= /// file: configuration.cc /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-06 10:32:43 /// dream: don't forget your dreams! /// ====================================== #ifndef __configuration_h__ #define __configuration_h__ #include "noncopyable.h" #include <string> #include <map> #define confpath "/home/wtp/spell/server/conf/configure.txt" using namespace std; namespace wd { class configuration :public noncopyable { public: configuration(const string &filepath); ~configuration()=default; string getdictdir() const; string getindexdir() const; string getip()const; string getcache() const; unsigned short getport() const; private: string _filepath; map<string,string> _conf; }; }; template<typename t> class singleton { public: template<typename ...args> static t *getinstance(args ...args) { if(!_pinstance) _pinstance=new t(args ...); return _pinstance; } static void destroy() { if(_pinstance) delete _pinstance; } private: singleton(); ~singleton(); static t *_pinstance; }; template<typename t> t *singleton<t>::_pinstance =null; #endif
///======================================= /// file: configuration.cc /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-07 15:24:14 /// dream: don't forget your dreams! /// ====================================== #include "configuration.h" #include <stdlib.h> #include <utility> #include <fstream> #include <iostream> using namespace std; using namespace wd; wd::configuration::configuration(const string & filepath) :_filepath(std::move(filepath)) { ifstream ifs(_filepath); if(!ifs){ cout<<"file open error!"<<endl; } string key,value; while(ifs>>key) { ifs>>value; _conf.insert(std::make_pair(key,value)); } ifs.close(); } string wd::configuration::getdictdir() const { auto cit=_conf.find("mydict"); if(cit== _conf.end()) return ""; else return cit->second; } string wd::configuration::getindexdir() const { auto cit =_conf.find("myindex"); if(cit== _conf.end()) return ""; else return cit->second; } string wd::configuration::getip() const { auto cit =_conf.find("myip"); if(cit ==_conf.end()) return ""; else return cit->second; } unsigned short wd::configuration::getport() const { auto cit =_conf.find("myport"); if(cit==_conf.end()) return 0; else return atoi(cit->second.c_str()); } string wd::configuration::getcache() const { auto cit =_conf.find("mycache"); if(cit ==_conf.end()) return ""; else return cit->second; }
==acceptor:==
///======================================= /// file: acceptor.h /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-05 23:47:05 /// dream: don't forget your dreams! /// ====================================== #ifndef __wd_acceptor_h__ #define __wd_acceptor_h__ #include "socket.h" #include "inetaddress.h" namespace wd { class acceptor { public: acceptor(int listenfd,const inetaddress & addr); void ready();//服务器监听准备 int accept();//服务器接收新连接 int fd()const {return _listensock.fd();} private: void setreuseaddr(bool on);//设置地址重用 void setreuseport(bool on);//设置端口重用 void bind();//绑定 void listen();//监听 socket _listensock; inetaddress _addr; }; } #endif
///======================================= /// file: acceptor.cc /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-05 23:52:12 /// dream: don't forget your dreams! /// ====================================== #include <iostream> #include "acceptor.h" #include "socketutil.h" namespace wd { acceptor::acceptor(int listenfd,const inetaddress & addr) :_listensock(listenfd) ,_addr(addr) {} void acceptor::ready() { setreuseaddr(true); setreuseport(true); bind(); listen(); } int acceptor::accept() { int peerfd=::accept(_listensock.fd(),null,null); if(-1==peerfd) { perror("accept error!"); } return peerfd; } void acceptor::setreuseaddr(bool flag) { int on=(flag?1:0); if(::setsockopt(_listensock.fd() ,sol_socket ,so_reuseaddr ,&on ,static_cast<socklen_t>(size_t(on)))==-1) { perror("setsockopt reuseaddr error!"); ::close(_listensock.fd()); exit(exit_failure); } } void acceptor::setreuseport(bool flag) { #ifndef so_reuseport int on=(flag?1:0); if(::setsockopt(_listensock.fd() ,sol_socket ,so_reuseaddr ,&on ,static_cast<socklen_t>(size_t(on)))==-1) { perror("setsockopt reuseport error!"); ::close(_listensock.fd()); exit(exit_failure); } #else if(flag) { fprintf(stderr,"so_reuseport is not supported!\n"); } #endif } void acceptor::bind() { if(-1==::bind(_listensock.fd() ,(const struct sockaddr*)_addr.getsockaddrptr() ,sizeof(inetaddress))) { perror("bind error!"); ::close(_listensock.fd()); exit(exit_failure); } } void acceptor::listen() { if(-1==::listen(_listensock.fd(),10)) { perror("listen error!"); ::close(_listensock.fd()); exit(exit_failure); } } } #if 0 int main() { std::cout<<"acceptor is correct!"<<std::endl; } #endif
==condition.h:==
#ifndef __wd_condition_h__ #define __wd_condition_h__ #include "noncopyable.h" #include "mutexlock.h" #include <pthread.h> namespace wd { class condition :noncopyable { public: condition(mutexlock &mutex) :_mutex(mutex) {pthread_cond_init(&_cond,null);} ~condition() {pthread_cond_destroy(&_cond);} void wait() {pthread_cond_wait(&_cond,_mutex.getmutexlockptr());} void notify() {pthread_cond_signal(&_cond);} void notifyall() {pthread_cond_broadcast(&_cond);} private: pthread_cond_t _cond; mutexlock & _mutex; }; } #endif
==cache:==
///======================================= /// file: cache.h /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-05 19:52:40 /// dream: don't forget your dreams! /// ====================================== #ifndef __cache_h__ #define __cache_h__ #include <unordered_map> #include <string> using namespace std; namespace wd { class cache { public: void addelement(string,string);//增加缓存信息 void readfromfile(string);//从文件中读取信息 void writetofile(string);//将信息写入文件中 void update(const cache&);//更新缓存消息 bool find(string querry);//从数据库中找寻信息 string &operator[](string key); private: unordered_map<string,string>_hashtable;//采用hashtable进行缓存 }; }; #endif
///======================================= /// file: cache.cc /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-05 20:01:25 /// dream: don't forget your dreams! /// ====================================== #include "cache.h" #include <fstream> #include <utility> #include <iostream> using namespace std; using namespace wd; void cache::addelement(string querry,string result) { _hashtable[querry]=result; } void cache::readfromfile(string filepath) { ifstream ifs(filepath); if(!ifs){ cout<<"cache::read readfromfile file open error!"<<endl; return; } string querry,result; while(ifs>>querry,!ifs.eof()) { ifs>>result; _hashtable[querry]=result; } } #if 0 void cache::writetofile(string filepath) { ofstream ofs(filepath); if(!ofs){ cout<<""<<endl; return; } for(auto &mypair:_hashtable) { ofs<<mypair.first<<" "; ofs<<mypair.second<<endl; } } void cache::update(const cache & cache) { for(auto &mypair:cache._hashtable) { auto cit =_hashtable.find(mypair.first); if(cit==_hashtable.end()) { _hashtable.insert(std::move(mypair)); } } } #endif void cache::writetofile(string filepath) { ofstream ofs(filepath); if(!ofs){ cout<<"file write error!"<<endl; return; } for(auto &mypair:_hashtable) { ofs<<mypair.first<<" "; ofs<<mypair.second<<endl; } } void cache::update(const cache & cache) { for(auto &mypair:cache._hashtable) { auto cit =_hashtable.find(mypair.first); if(cit==_hashtable.end()) { _hashtable.insert(std::move(mypair)); } } } bool cache::find(string querry) { auto cit =_hashtable.find(querry); if(cit==_hashtable.end()) return false; return true; } string &cache::operator[](string key) { return _hashtable[key]; } #if 0 int main() { cout<<"cache is correct!"<<endl; } #endif
==cachemanger:==
///======================================= /// file: cachemanger.h /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-05 20:51:09 /// dream: don't forget your dreams! /// ====================================== #ifndef __cachemanger_h__ #define __cachemanger_h__ #include "cache.h" #include <vector> #define threadnum 4//线程数目设置为4个,可自定义 using std::vector; namespace wd { class cachemanger { public: cachemanger(string filepath); void init(string);//创建缓存 cache & getcache(size_t);//获取某个缓存 void periodicupdate();//定时更新所有缓存 private: string _cachefilepath; vector<cache>_cachelist; }; }; #endif
///======================================= /// file: cachemanger.cc /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-05 20:56:50 /// dream: don't forget your dreams! /// ====================================== #include "cachemanger.h" #include <iostream> #include <fstream> #include <utility> #include <iostream> using namespace std; using namespace wd; cachemanger::cachemanger(string cachefilepath) { init(cachefilepath); } void cachemanger::init(string cachefilepath) { _cachefilepath=cachefilepath; _cachelist.reserve(threadnum); cache tmp; tmp.readfromfile(_cachefilepath); for(size_t i=0;i!=threadnum;++i) { _cachelist.push_back(std::move(tmp)); } } cache & cachemanger::getcache(size_t number) { return _cachelist[number]; } void cachemanger::periodicupdate() { auto cit=_cachelist.begin(); cache lastwrite=*(cit ++); for(;cit<_cachelist.end();++cit) { lastwrite.update(*cit); } for(cit=_cachelist.begin()+1;cit!=_cachelist.end();++cit) { (*cit).update(lastwrite); } lastwrite.writetofile(_cachefilepath); }
==epollpoller:==
///======================================= /// file: epollpoller.h /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-06 11:03:36 /// dream: don't forget your dreams! /// ====================================== #ifndef __wd_epollpoller_h__ #define __wd_epollpoller_h__ #include "tcpconnection.h" #include "noncopyable.h" #include "mutexlock.h" #include <sys/epoll.h> #include <vector> #include <map> #include <functional> namespace wd { class acceptor; class epollpoller :noncopyable { public: typedef tcpconnection::tcpconnectioncallback epollcallback; typedef std::function<void()> functor; epollpoller(acceptor &acceptor); ~epollpoller(); void loop(); void unloop(); void runinloop(const functor && cb); void dopendingfunctors(); void wakeup(); void setconnectioncallback(epollcallback cb); void setmessagecallback(epollcallback cb); void setclosecallback(epollcallback cb); private: void waitepollfd(); void handleconnection(); void handlemessage(int peerfd); void handleread(); acceptor & _acceptor; int _epollfd; int _eventfd; int _listenfd; bool _islooping; mutexlock _mutex; std::vector<functor> _pendingfunctors; typedef std::vector<struct epoll_event>eventlist; eventlist _eventlist; typedef std::map<int,tcpconnectionptr> connectionmap; connectionmap _connmap; epollcallback _onconnectioncb; epollcallback _onmessagecb; epollcallback _onclosecb; }; } #endif
///======================================= /// file: epollpoller.cc /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-07 15:42:54 /// dream: don't forget your dreams! /// ====================================== #include "epollpoller.h" #include "socketutil.h" #include "acceptor.h" #include <assert.h> #include <iostream> using namespace std; namespace wd { epollpoller::epollpoller(acceptor & acceptor) :_acceptor(acceptor) ,_epollfd(createepollfd()) ,_eventfd(createeventfd()) ,_listenfd(_acceptor.fd()) ,_islooping(false) ,_eventlist(1024) { addepollfdread(_epollfd,_listenfd); addepollfdread(_epollfd,_eventfd); } epollpoller::~epollpoller() { ::close(_epollfd); } void epollpoller::loop() { _islooping=true; while(_islooping) { waitepollfd(); } } void epollpoller::unloop() { if(_islooping) _islooping=false; } void epollpoller::setconnectioncallback(epollcallback cb) { _onconnectioncb=cb; } void epollpoller::setmessagecallback(epollcallback cb) { _onmessagecb=cb; } void epollpoller::setclosecallback(epollcallback cb) { _onclosecb=cb; } void epollpoller::waitepollfd() { int nready; do { nready =::epoll_wait(_epollfd,&(*_eventlist.begin()),_eventlist.size(),10000); }while(-1==nready && errno ==eintr); if(-1==nready){ perror("epoll wait error!"); exit(exit_failure); }else if(0==nready){ cout<<"epoll_wait timeout!"<<endl; }else{//扩容 if(nready==static_cast<int>(_eventlist.size())){ _eventlist.resize(_eventlist.size()*2); } for(int idx=0;idx!=nready;++idx)//正宗罗老师循环体(twt) { if(_eventlist[idx].data.fd ==_listenfd) { if(_eventlist[idx].events & epollin) { handleconnection(); } }else if(_eventlist[idx].data.fd ==_eventfd){ handleread(); cout<<">>dopendingfunctors()"<<endl; dopendingfunctors(); }else{ if(_eventlist[idx].events & epollin){ handlemessage(_eventlist[idx].data.fd); } } } } } void epollpoller::handleconnection() { int peerfd=_acceptor.accept(); addepollfdread(_epollfd,peerfd); tcpconnectionptr conn(new tcpconnection(peerfd,this)); conn->setconnectioncallback(_onconnectioncb); conn->setmessagecallback(_onmessagecb); conn->setclosecallback(_onclosecb); std::pair<connectionmap::iterator,bool>ret; ret=_connmap.insert(std::make_pair(peerfd,conn)); assert(ret.second ==true); (void)ret; conn->handleconnectioncallback(); } void epollpoller::handlemessage(int peerfd) { bool isclosed=isconnectionclosed(peerfd); connectionmap::iterator it =_connmap.find(peerfd); assert(it!=_connmap.end()); if(isclosed) { it->second->handleclosecallback(); delepollreadfd(_epollfd,peerfd); _connmap.erase(it); }else{ it->second->handlemessagecallback(); } } void epollpoller::runinloop(const functor && cb)//在计算线程中执行 { mutexlockguard mlg(_mutex); _pendingfunctors.push_back(std::move(cb)); wakeup(); } void epollpoller::dopendingfunctors() { std::vector<functor>tmp; { mutexlockguard mlg(_mutex); tmp.swap(_pendingfunctors); } for(auto & functor:tmp) { functor(); } } void epollpoller::handleread() { uint64_t howmany; int ret=::read(_eventfd,&howmany,sizeof(howmany)); if(ret !=sizeof(howmany)) { perror("read error!"); } } void epollpoller::wakeup() { uint64_t one =1; int ret =::write(_eventfd,&one,sizeof(one)); if(ret!=sizeof(one)) { perror("write error!"); } } }
==inetaddress:==
///======================================= /// file: inetaddress.h /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-05 21:55:19 /// dream: don't forget your dreams! /// ====================================== #ifndef __wd_inetaddress_h__ #define __wd_inetaddress_h__ #include <netinet/in.h> #include <string> namespace wd { class inetaddress { public: inetaddress(short port); inetaddress(const char *pip,short port); inetaddress(const struct sockaddr_in & addr); std::string ip()const; unsigned short port() const; const struct sockaddr_in *getsockaddrptr() const; private: struct sockaddr_in _addr; }; } #endif
///======================================= /// file: inetaddress.cc /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-07 20:55:18 /// dream: don't forget your dreams! /// ====================================== #include "inetaddress.h" #include <stdio.h> #include <stdlib.h> #include <sys/socket.h> #include <arpa/inet.h> #include <string.h> namespace wd { inetaddress::inetaddress(short port) { ::memset(&_addr,0,sizeof(_addr)); _addr.sin_family=af_inet; _addr.sin_port=htons(port); _addr.sin_addr.s_addr=inaddr_any; } inetaddress::inetaddress(const char * pip,short port) { ::memset(&_addr,0,sizeof(_addr)); _addr.sin_family=af_inet; _addr.sin_port=htons(port); _addr.sin_addr.s_addr=inet_addr(pip); } inetaddress::inetaddress(const struct sockaddr_in & addr) :_addr(addr) {} const struct sockaddr_in * inetaddress::getsockaddrptr()const { return & _addr; } std::string inetaddress::ip()const { return std::string(inet_ntoa(_addr.sin_addr)); } unsigned short inetaddress::port() const { return ntohs(_addr.sin_port); } }
==mutexlock:==
#ifndef __wd_mutexlock_h__ #define __wd_mutexlock_h__ #include "noncopyable.h" #include <pthread.h> namespace wd { class mutexlock :noncopyable { public: mutexlock() {pthread_mutex_init(&_mutex,null);} ~mutexlock() {pthread_mutex_destroy(&_mutex);} void lock() {pthread_mutex_lock(&_mutex);} void unlock() {pthread_mutex_unlock(&_mutex);} pthread_mutex_t *getmutexlockptr() {return &_mutex;} private: pthread_mutex_t _mutex; }; class mutexlockguard//c++之父bs提出的raii { public: mutexlockguard(mutexlock &mutex) :_mutex(mutex) { _mutex.lock(); } ~mutexlockguard() { _mutex.unlock(); } private: mutexlock &_mutex; }; } #endif
==mydict:==
///======================================= /// file: mydict.h /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-06 11:12:19 /// dream: don't forget your dreams! /// ====================================== #ifndef __mydict_h__ #define __mydict_h__ #include <string> #include <vector> #include <map> #include <utility> #include <set> #include <fstream> #include <iostream> #include <sstream> using namespace std; namespace wd { struct myresult { string _word; int _ifreq;//词频 int _idist;//最小编辑距离 }; class mydict { public: mydict(const string dictdir,const string indexdir) { ifstream ifs1(dictdir),ifs2(indexdir); if(!ifs1||!ifs2) cout<<"mydict open file error!"<<endl; string key; int value; ifs1>>value; _dict.push_back(std::make_pair(string(" "),value)); ifs1>>value; _dict.push_back(std::make_pair(string(" "),value)); while(ifs1>>key) { ifs1>>value; _dict.push_back(std::make_pair(key,value)); } string line; while(std::getline(ifs2,line)) { istringstream iss(line); string ikey; int ivalue; iss>>ikey; set<int> tmp; while(iss>>ivalue) { tmp.insert(ivalue); } _index.insert(std::make_pair(ikey,tmp)); } } vector<pair<string,int>> & getdict(){return _dict;} map<string ,set<int>> & getindextable(){return _index;} private: vector<pair<string,int>> _dict; map<string,set<int>> _index; }; } #endif
==mytask:==
///======================================= /// file: mytask.h /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-06 21:04:54 /// dream: don't forget your dreams! /// ====================================== #ifndef __mytask_h__ #define __mytask_h__ #include "tcpconnection.h" #include "configuration.h" #include "mydict.h" #include "cache.h" #include <string> #include <queue> using namespace std; class mycompare { public: bool operator()(const wd::myresult & lhs,const wd::myresult &rhs) { if(lhs._idist !=rhs._idist) return lhs._idist<rhs._idist; else return lhs._ifreq>rhs._ifreq; } private: }; using character =string; class mytask { public: mytask(const string &querry,const wd::tcpconnectionptr conn) :_querry(std::move(querry)) ,_conn(conn) {} void execute(); private: void queryindextable();//查询索引(四个索引) void statistic(set<int> &iset);//计算 int distance(const string & rhs);//计算最小编辑距离 bool response(wd::cache & cache);//响应客户端的请求 vector<character>getonecharacter(const string &word);//获取字符数组 string _querry; wd::tcpconnectionptr _conn; priority_queue<wd::myresult,vector<wd::myresult>,mycompare> _resultque; }; #endif
///======================================= /// file: mytask.cc /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-07 22:47:19 /// dream: don't forget your dreams! /// ====================================== #include "mytask.h" #include "configuration.h" #include "mydict.h" #include "cachemanger.h" #include "json/json.h" #include <string.h> #include <algorithm> extern __thread int t_number; bool mytask::response(wd::cache &cache) { if(cache.find(_querry)) { _conn->sendinloop(cache[_querry]); return true; } return false; } int mytask::distance(const string &rhs) { vector<character>querrycharacter =getonecharacter(_querry); vector<character>indexcharacter =getonecharacter(rhs); int len1,len2; len1=querrycharacter.size(); len2=indexcharacter.size(); int edit[len1+1][len2+1]; int i,j; for(i=0;i<=len1;++i){ for(j=0;j<=len2;++j){ edit[i][j]=0; } } for(i=0;i<len1;++i){ edit[i][0]=i; } for(j=0;j<=len2;++j){ edit[0][j]=j; } for(i=1;i<len1;++i){ for(j=1;j<=len2;++j){ int cost =((querrycharacter[i-1]==indexcharacter[j-1])?0:1); int deletion =edit[i-1][j]+1; int insertion=edit[i][j-1]+1; int substitution=edit[i-1][j-1]+cost; edit[i][j]=std::min(deletion,std::min(insertion,substitution)); } } return edit[len1][len2]; } void mytask::statistic(set<int> &iset) { vector<pair<string,int>>dict=(singleton<wd::mydict>::getinstance(singleton<wd::configuration>::getinstance(confpath)->getdictdir(), singleton<wd::configuration>::getinstance(confpath)->getindexdir()))->getdict(); for(auto &idx:iset) { string key=dict[idx].first; int idist =distance(key); if(idist<=3) { wd::myresult res; res._word=key; res._idist=idist; res._ifreq=dict[idx].second; _resultque.push(res); } } } vector<character>mytask::getonecharacter(const string & word) { auto cit =word.begin(); vector<character> ret; while(cit<word.end()) { if(224==(*cit &224)){ character onecharacter; onecharacter.append(cit,cit+3); ret.push_back(onecharacter); cit =cit+3; }else if(240==(*cit &240)){ character onecharacter; onecharacter.append(cit,cit+4); ret.push_back(onecharacter); cit =cit+4; }else{ character onecharacter(1,*cit); ret.push_back(onecharacter); cit ++; } } return ret; } void mytask::queryindextable() { map<string,set<int>>index=(singleton<wd::mydict>::getinstance(singleton<wd::configuration>::getinstance(confpath)->getdictdir(), singleton<wd::configuration>::getinstance(confpath)->getindexdir()))->getindextable(); vector<character> onecharacter=getonecharacter(_querry); set<int>allrally; for(auto mycharacter:onecharacter) { auto cit =index.find(mycharacter); if(cit!=index.end()) { for(auto &idx:cit->second) allrally.insert(idx); } } statistic(allrally); } void mytask::execute() { wd::cache &mycache =(singleton<wd::cachemanger>::getinstance(singleton<wd::configuration>::getinstance(confpath)->getcache()))->getcache(t_number); if(response(mycache)) return; else{ queryindextable(); json::fastwriter writerinfo; json::value arrayobj; while(!_resultque.empty()) { json::value new_item; new_item[""]=_resultque.top()._word; _resultque.pop(); arrayobj.append(new_item); } string stremail=writerinfo.write(arrayobj); mycache.addelement(_querry,stremail); _conn->sendinloop(stremail); } }
==noncopyable:==
#ifndef __wd_noncopyable_h__ #define __wd_noncopyable_h__ namespace wd { class noncopyable { protected: noncopyable(){} ~noncopyable(){} private: noncopyable(const noncopyable &); noncopyable & operator=(const noncopyable &); }; } #endif
==socket:==
///======================================= /// file: socket.h /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-05 21:46:26 /// dream: don't forget your dreams! /// ====================================== #ifndef __wd_socket_h__ #define __wd_socket_h__ #include "noncopyable.h" namespace wd { class inetaddress; class socket :noncopyable { public: socket(int socket); socket(); ~socket(); void shutdownwrite(); int fd()const {return _sockfd;} void nonblock(); static inetaddress getlocaladdr(int socketfd); static inetaddress getpeeraddr(int sockfd); private: int _sockfd; }; } #endif
///======================================= /// file: socket.cc /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-07 20:38:20 /// dream: don't forget your dreams! /// ====================================== #include "socket.h" #include "socketutil.h" #include "inetaddress.h" namespace wd { socket::socket(int sockfd) :_sockfd(sockfd) {} socket::socket() :_sockfd(createsocketfd()) {} socket::~socket() { ::close(_sockfd); } void socket::nonblock() { setnonblock(_sockfd); } void socket::shutdownwrite() { if(-1==::shutdown(_sockfd,shut_wr)){ perror("shutdown write error!"); } } inetaddress socket::getlocaladdr(int sockfd) { struct sockaddr_in addr; socklen_t len=sizeof(sockaddr_in); if(-1==::getsockname(sockfd,(struct sockaddr *)&addr,&len)){ perror("getsockname error!"); } return inetaddress(addr); } inetaddress socket::getpeeraddr(int sockfd) { struct sockaddr_in addr; socklen_t len=sizeof(sockaddr_in); if(-1==::getpeername(sockfd,(struct sockaddr *)&addr,&len)){ perror("getpeername error!"); } return inetaddress(addr); } }
==socketio:==
///======================================= /// file: socketio.h /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-06 17:10:23 /// dream: don't forget your dreams! /// ====================================== #ifndef __socketio_h__ #define __socketio_h__ #include <stdio.h> namespace wd { class socketio { public: socketio(int sockfd); size_t readn(char *buf,size_t count); size_t writen(const char *buf,size_t count); size_t readline(char *buf,size_t max_len); private: size_t recv_peek(char *buf,size_t count); int _sockfd; }; } #endif
///======================================= /// file: socketio.cc /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-07 21:56:34 /// dream: don't forget your dreams! /// ====================================== #include "socketio.h" #include "socketutil.h" namespace wd { socketio::socketio(int sockfd) :_sockfd(sockfd) {} size_t socketio::readn(char *buf,size_t count) { size_t nleft =count; char *pbuf=buf; while(nleft>0) { int nread =::read(_sockfd,pbuf,nleft); if(-1==nread) { if(errno ==eintr) continue; return exit_failure; }else if(0==nread){ break; } pbuf =pbuf+nread; nleft=nleft-nread; } return (count -nleft); } size_t socketio::writen(const char * buf,size_t count) { size_t nleft =count; const char *pbuf=buf; while(nleft >0) { int nwrite=::write(_sockfd,pbuf,nleft); if(-1==nwrite) { if(errno ==eintr) continue; return exit_failure; } nleft =nleft -nwrite; pbuf =pbuf +nwrite; } return (count -nleft); } size_t socketio::recv_peek(char *buf,size_t count) { int nread; do{ nread=::recv(_sockfd,buf,count,msg_peek); }while(-1==nread && errno ==eintr); return nread; } size_t socketio::readline(char *buf,size_t maxlen) { size_t nleft =maxlen-1; char *pbuf=buf; size_t total=0; while(nleft>0) { size_t nread =recv_peek(pbuf,nleft); if(nread<=0) return nread; for(size_t idx =0;idx!=nread;++idx){//检查换行符/n if(pbuf[idx]=='\n'){ size_t nsize =idx +1; if(readn(pbuf,nsize)!=nsize) return exit_failure; pbuf +=nsize; total +=nsize; *pbuf=0; return total; } } if(readn(pbuf,nread)!=nread) return exit_failure; pbuf +=nread; nleft -=nread; total +=nread; } *pbuf=0; return maxlen-1; } }
==sockutil(根据陈硕编写的linux书上分开头文件)==
///======================================= /// file: socktutil.h /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-05 22:01:30 /// dream: don't forget your dreams! /// ====================================== #ifndef __wd_sockerutil_h__ #define __wd_sockerutil_h__ #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/eventfd.h> #include <sys/epoll.h> namespace wd { inline int createsocketfd() { int fd=::socket(af_inet,sock_stream,0); if(-1==fd) { perror("socket create error!"); } return fd; } inline void setnonblock(int fd) { int flags=::fcntl(fd,f_getfl,0); flags |= o_nonblock; ::fcntl(fd,f_setfl,flags); } inline int createepollfd() { int efd=::epoll_create1(0); if(-1==efd) { perror("epoll create1 error!"); exit(exit_failure); } return efd; } inline int createeventfd() { int evtfd=::eventfd(0,efd_nonblock|efd_cloexec); if(-1==evtfd) { perror("eventfd create error!"); } return evtfd; } inline void addepollfdread(int efd,int fd) { struct epoll_event ev; ev.data.fd=fd; ev.events=epollin; int ret=epoll_ctl(efd,epoll_ctl_add,fd,&ev); if(-1==ret) { perror("epoll ctl add error!"); exit(exit_failure); } } inline void delepollreadfd(int efd,int fd) { struct epoll_event ev; ev.data.fd=fd; int ret=epoll_ctl(efd,epoll_ctl_del,fd,&ev); if(-1==ret) { perror("epoll ctl delete error!"); exit(exit_failure); } } inline size_t recvpeek(int sockfd,void *buf,size_t len) { int nread; do{ nread=::recv(sockfd,buf,len,msg_peek); }while(nread==-1 && errno ==eintr); return nread; } inline bool isconnectionclosed(int sockfd) { char buf[1024]; int nread=recvpeek(sockfd,buf,sizeof(buf)); if(-1==nread) { perror("recvpeek error!"); return true; } return (0==nread); } } #endif
==spellcorrrectsever:==
///======================================= /// file: spellcorrectserver.h /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-06 20:41:13 /// dream: don't forget your dreams! /// ====================================== #ifndef __spellcorrectserver_h__ #define __spellcorrectserver_h__ #include "tcpserver.h" #include "threadpool.h" using namespace wd; namespace wd { class spellcorrectserver { public: spellcorrectserver(const string & ip ,unsigned short port ,size_t threadnum ,size_t quesize); void start(); private: void onconnection(const tcpconnectionptr &); void onmessage(const tcpconnectionptr &); void onclose(const tcpconnectionptr &); tcpserver _tcpserver; threadpool _threadpoll; }; }; #endif
///======================================= /// file: spellcorrectserver.cc /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-07 21:20:41 /// dream: don't forget your dreams! /// ====================================== #include "spellcorrectserver.h" #include "mytask.h" #include <stdio.h> #include <iostream> #include <string> #include <utility> #include <functional> using namespace std; void spellcorrectserver::onconnection(const wd::tcpconnectionptr & conn) { cout<<conn->tostring()<<endl; conn->send("hello ,welcome to wtp chat server.\r\n"); } void spellcorrectserver::onmessage(const wd::tcpconnectionptr & conn) { string s(conn->receive()); mytask task(s,conn); _threadpoll.addtask(std::bind(&mytask::execute,&task)); cout<<">add task to threadpool"<<endl; } void spellcorrectserver::onclose(const wd::tcpconnectionptr &conn) { ::printf("%s close\n",conn->tostring().c_str()); } spellcorrectserver::spellcorrectserver(const string & ip ,unsigned short port ,size_t threadnum ,size_t quesize) :_tcpserver(ip,port) ,_threadpoll(threadnum,quesize) {} void spellcorrectserver::start() { _threadpoll.start(); _tcpserver.setconnectioncallback(std::bind(&spellcorrectserver::onconnection,this,std::placeholders::_1)); _tcpserver.setmessagecallback(std::bind(&spellcorrectserver::onmessage,this,std::placeholders::_1)); _tcpserver.setclosecallback(std::bind(&spellcorrectserver::onclose,this,std::placeholders::_1)); _tcpserver.start(); }
==taskque:==
#ifndef __wd_taskqueue_h__ #define __wd_taskqueue_h__ #include "mutexlock.h" #include "condition.h" #include <queue> #include <functional> namespace wd { typedef std::function<void()>task; class taskqueue { public: taskqueue(size_t quesize) :_quesize(quesize) ,_mutex() ,_notfull(_mutex) ,_notempty(_mutex) ,_flag(true) {} void push(task &&task); task pop(); bool empty()const { return _que.size()==0; } bool full()const {return _que.size()==_quesize;} void wakeup() { if(_flag) _flag=false; _notempty.notifyall(); } private: size_t _quesize; std::queue<task> _que; mutexlock _mutex; condition _notfull; condition _notempty; bool _flag; }; } #endif
#include "taskqueue.h" using namespace wd; //生产者所在的线程 void taskqueue::push(task && task) { mutexlockguard autolock(_mutex); while(full()) { _notfull.wait(); } _que.push(std::move(task)); _notempty.notify(); } //消费者所在线程 task taskqueue::pop() { mutexlockguard autolock(_mutex); while(_flag && empty()) { _notempty.wait(); } if(_flag){ task task=_que.front(); _que.pop(); _notfull.notify(); return task; }else{ return null; } } #if 0 task taskqueue::pop() { mutexlockguard autolock(_mutex); while(_flag && empty()) { _notempty.wait(); } if(_flag){ task task =_que.front(); _que.pop(); _notfull.notify(); return task; }else{ return null; } } #endif
==tcpconnection:==
///======================================= /// file: tcpconnection.h /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-06 17:15:33 /// dream: don't forget your dreams! /// ====================================== #ifndef __wd_tcpconnection_h__ #define __wd_tcpconnection_h__ #include "noncopyable.h" #include "inetaddress.h" #include "socket.h" #include "socketio.h" #include <string> #include <memory> #include <functional> namespace wd { class epollpoller; class tcpconnection; typedef std::shared_ptr<tcpconnection> tcpconnectionptr; class tcpconnection :noncopyable ,public std::enable_shared_from_this<tcpconnection> { public: typedef std::function<void(const tcpconnectionptr &)>tcpconnectioncallback; tcpconnection(int sockfd,epollpoller *loop); ~tcpconnection(); std::string receive(); void send(const std::string &msg); void sendinloop(const std::string &msg); void shutdown(); std::string tostring(); void setconnectioncallback(tcpconnectioncallback cb); void setmessagecallback(tcpconnectioncallback cb); void setclosecallback(tcpconnectioncallback cb); void handleconnectioncallback(); void handlemessagecallback(); void handleclosecallback(); private: socket _sockfd; socketio _sockio; const inetaddress _localaddr; const inetaddress _peeraddr; bool _isshutdownwrite; epollpoller * _loop; tcpconnectioncallback _onconnectioncb; tcpconnectioncallback _onmessagecb; tcpconnectioncallback _onclosecb; }; } #endif
///======================================= /// file: tcpconnection.cc /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-07 22:22:22 /// dream: don't forget your dreams! /// ====================================== #include "tcpconnection.h" #include "epollpoller.h" #include <string.h> #include <stdio.h> namespace wd { tcpconnection::tcpconnection(int sockfd,epollpoller * loop) :_sockfd(sockfd) ,_sockio(sockfd) ,_localaddr(wd::socket::getlocaladdr(sockfd)) ,_peeraddr(wd::socket::getpeeraddr(sockfd)) ,_isshutdownwrite(false) ,_loop(loop) {_sockfd.nonblock();} tcpconnection::~tcpconnection() { if(!_isshutdownwrite) { _isshutdownwrite=true; shutdown(); } printf("~tcpconnection()\n"); } std::string tcpconnection::receive() { char buf[65536]; memset(buf,0,sizeof(buf)); size_t ret =_sockio.readline(buf,sizeof(buf)); if(0==ret){ return std::string(); }else{ return std::string(buf); } } void tcpconnection::send(const std::string &msg) { size_t len=msg.size(); _sockio.writen((const char *)&len,sizeof(int)); _sockio.writen(msg.c_str(),len); } void tcpconnection::shutdown() { if(!_isshutdownwrite) _sockfd.shutdownwrite(); _isshutdownwrite=true; } std::string tcpconnection::tostring() { char str[100]; snprintf(str,sizeof(str),"%s:%d->%s:%d" ,_localaddr.ip().c_str() ,_localaddr.port() ,_peeraddr.ip().c_str() ,_peeraddr.port()); return std::string(str); } void tcpconnection::setconnectioncallback(tcpconnectioncallback cb) { _onconnectioncb =cb; } void tcpconnection::setmessagecallback(tcpconnectioncallback cb) { _onmessagecb =cb; } void tcpconnection::setclosecallback(tcpconnectioncallback cb) { _onclosecb =cb; } void tcpconnection::handleconnectioncallback() { if(_onconnectioncb){ _onconnectioncb(shared_from_this()); } } void tcpconnection::handlemessagecallback() { if(_onmessagecb){ _onmessagecb(shared_from_this()); } } void tcpconnection::handleclosecallback() { if(_onclosecb){ _onclosecb(shared_from_this()); } } void tcpconnection::sendinloop(const std::string & msg) { _loop->runinloop(std::bind(&tcpconnection::send,this,msg)); } }
==tcpserver:==
///======================================= /// file: tcpserver.h /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-06 20:15:21 /// dream: don't forget your dreams! /// ====================================== #ifndef __wd_tcpserver_h__ #define __wd_tcpserver_h__ #include "acceptor.h" #include "epollpoller.h" #include <string> using std::string; namespace wd { class tcpserver { public: typedef epollpoller::epollcallback tcpservercallback; tcpserver(const string & ip,unsigned short port); tcpserver(unsigned short port); void start(); void stop(); void setconnectioncallback(tcpservercallback cb); void setmessagecallback(tcpservercallback cb); void setclosecallback(tcpservercallback cb); private: acceptor _acceptor; epollpoller _poller; tcpservercallback _connectioncallback; tcpservercallback _messagecallback; tcpservercallback _closecallback; }; } #endif
///======================================= /// file: tcpserver.cc /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-07 19:59:37 /// dream: don't forget your dreams! /// ====================================== #include "tcpserver.h" #include "inetaddress.h" #include "socketutil.h" #include <iostream> using namespace std; namespace wd { tcpserver::tcpserver(const string & ip,unsigned short port) :_acceptor(createsocketfd(),inetaddress(ip.c_str(),port)) ,_poller(_acceptor) {} void tcpserver::start() { _acceptor.ready(); _poller.setconnectioncallback(_connectioncallback); _poller.setmessagecallback(_messagecallback); _poller.setclosecallback(_closecallback); _poller.loop(); } void tcpserver::stop() { _poller.unloop(); } void tcpserver::setconnectioncallback(tcpservercallback cb) {_connectioncallback=cb;} void tcpserver::setmessagecallback(tcpservercallback cb) {_messagecallback=cb;} void tcpserver::setclosecallback(tcpservercallback cb) {_closecallback=cb;} }
==thread:==
#ifndef __wd_thread_h__ #define __wd_thread_h__ #include "noncopyable.h" #include <pthread.h> #include <functional> using std::function; namespace wd { class thread; struct threadptr { int _number; thread *_pthread; }; class thread :noncopyable { using threadcallback =function<void()>; public: thread(threadcallback &&cb); ~thread(); void start(int number); void join(); bool isrunning()const {return _isrunning;} private: static void * threadfunc(void *); pthread_t _pthid; bool _isrunning; threadcallback _cb; }; } #endif
#include "thread.h" #include <iostream> using namespace std; using namespace wd; __thread int t_number;//将线程编号作为线程存储的标记 thread::thread(threadcallback && cb)//这里的右值引用本身取决于是否有名字 :_pthid(0) ,_isrunning(false) ,_cb(std::move(cb)) { cout<<"thread(cb)"<<endl; } void thread::start(int number) { threadptr *threadptr=new threadptr(); threadptr->_number=number; threadptr->_pthread=this; pthread_create(&_pthid,null,threadfunc,threadptr); _isrunning=true; } void *thread::threadfunc(void *arg) {//应用了线程存储 threadptr *threadptr=static_cast<threadptr*>(arg); thread * pthread=threadptr->_pthread; t_number=threadptr->_number; if(pthread) pthread->_cb();//线程开始工作! delete threadptr; //thread * pthread =threadptr->_pthread; return null; } #if 0 void *thread::threadfunc(void *arg) { threadptr *threadptr =static_cast<threadptr*>(arg); thread * pthread =threadptr->_pthread; t_number =threadptr->_number; if(pthread) pthread->_cb(); delete threadptr; return null; } #endif void thread::join() { pthread_join(_pthid,null); _isrunning=false; } thread::~thread() { if(_isrunning) { pthread_detach(_pthid); _isrunning=false; } cout<<"~thread()"<<endl; }
==threadpool:==
#ifndef __wd_threadpoll_h__ #define __wd_threadpoll_h__ #include "taskqueue.h" #include "thread.h" #include <vector> #include <memory> #include <functional> using std::shared_ptr; using std::vector; namespace wd { class threadpool { public: using task=std::function<void()>; threadpool(size_t threadnum,size_t quesize) :_threadnum(threadnum) ,_quesize(quesize) ,_taskque(_quesize) ,_isexit(false) { _threads.reserve(_threadnum); } ~threadpool(); void start(); void stop(); void addtask(task && task); private: void threadfunc(); task gettask(); size_t _threadnum; size_t _quesize; vector<shared_ptr<thread>> _threads; taskqueue _taskque; bool _isexit; }; } #endif
#include "threadpool.h" #include "thread.h" #include <unistd.h> #include <iostream> using namespace std; using namespace wd; void threadpool::start() { for(size_t idx=0;idx<_threadnum;++idx) { shared_ptr<thread>pthread(new thread(std::bind(&threadpool::threadfunc,this))); _threads.push_back(std::move(pthread)); } int number=0; for(auto &pthread:_threads) { pthread->start(number); ++number; } } void threadpool::stop()//为了线程安全,将stop方法置于主线程中 { if(!_isexit) { while(!_taskque.empty()){ ::sleep(1); cout<<"threadpool sleep 1 second!"<<endl; } _isexit=true; cout<<"threadpool ->stop:_isexit="<<_isexit<<endl; _taskque.wakeup(); for(auto &pthread:_threads){ pthread->join(); } } } threadpool::~threadpool() { if(!_isexit){ stop(); } } void threadpool::addtask(task && task) { _taskque.push(std::move(task)); } task threadpool::gettask() { return _taskque.pop(); } void threadpool::threadfunc() { while(!_isexit) { task task=gettask(); if(task){ task(); } } }
==timer:==
///======================================= /// file: timer.h /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-06 20:00:45 /// dream: don't forget your dreams! /// ====================================== #ifndef __wd_timer_h__ #define __wd_timer_h__ #include <functional> namespace wd { class timer { public: using timercallback =std::function<void()>; timer(int initailtime,int intervaltime,timercallback && cb); ~timer(); void start(); void stop(); private: int _fd; int _initialtime; int _intervaltime; timercallback _cb; bool _isstarted; int createtimerfd(); void settimerfd(int initialtime, int intervaltime); void handleread(); }; } #endif
///======================================= /// file: timer.cc /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-07 20:09:14 /// dream: don't forget your dreams! /// ====================================== #include "timer.h" #include <unistd.h> #include <errno.h> #include <poll.h> #include <sys/timerfd.h> #include <iostream> using namespace std; using namespace wd; timer::timer(int initialtime,int intervaltime,timercallback && cb) :_fd(createtimerfd()) ,_initialtime(initialtime) ,_intervaltime(intervaltime) ,_cb(std::move(cb)) ,_isstarted(false) {} void timer::start() { struct pollfd pfd; pfd.fd=_fd; pfd.events=pollin; settimerfd(_initialtime,_intervaltime);//开启定时器 _isstarted=true; while(_isstarted){ int nready=::poll(&pfd,1,5000); if(-1==nready &&errno ==eintr){ continue; }else if(-1==nready){ perror(">>>poll error!"); exit(exit_failure); }else if(0==nready){ cout<<">>>poll timeout!"<<endl; }else{ if(pfd.revents & pollin){ handleread();//先对定时器进行处理 if(_cb){ _cb();//再去执行回调任务 } } } } } void timer::stop() { settimerfd(0,0); if(_isstarted){ _isstarted=false; } } timer::~timer() { if(_isstarted){ stop(); } } int timer::createtimerfd() { int fd=::timerfd_create(clock_realtime,0); if(-1==fd){ perror(">>timerfd_create error!"); } return fd; } void timer::settimerfd(int initialtime,int intervaltime) { struct itimerspec value; value.it_value.tv_sec=initialtime; value.it_value.tv_nsec=0; value.it_interval.tv_sec=intervaltime; value.it_interval.tv_nsec=0; int ret=::timerfd_settime(_fd,0,&value,null); if(-1==ret){ perror(">>>timerfd_settime error!"); } } #if 0 void timer::handleread() { uint64_t howmany; int ret =::read(_fd,&howmany,sizeof(uint64_t)); if(ret!=sizeof(uint64_t)){ perror("read!"); } } #endif void timer::handleread() { uint64_t howmany;//为一个64位 int ret=::read(_fd,&howmany,sizeof(uint64_t)); if(ret!=sizeof(uint64_t)){ perror(">>>read error!"); } }
==timerthread:==
///======================================= /// file: timerthread.cc /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-07 17:12:51 /// dream: don't forget your dreams! /// ====================================== #ifndef __wd_timerthread_h__ #define __wd_timerthread_h__ #include "timer.h" #include "thread.h" #include <functional> namespace wd { class timerthread { public: using timercallback = std::function<void()>; timerthread(int, int, timercallback && cb); ~timerthread(); void start(); void stop(); private: timer _timer; thread _subthread; bool _isstarted; }; }//end of namespace wd #endif
///======================================= /// file: timerthread.cc /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-07 20:09:14 /// dream: don't forget your dreams! /// ====================================== #include "timerthread.h" using namespace wd; timerthread::timerthread(int initialtime, int intervaltime, timercallback && cb) : _timer(initialtime, intervaltime, std::move(cb)) , _subthread(std::bind(&timer::start, &_timer)) , _isstarted(false) {} void timerthread::start() { _subthread.start(0); _isstarted = true; } void timerthread::stop() { if(_isstarted) { _timer.stop(); _subthread.join(); _isstarted = false; } } timerthread::~timerthread() { if(_isstarted) stop(); }
==main:==
///======================================= /// file: main.cc /// author: wtptorres(1584292712@qq.com) /// date: 2019-06-07 21:09:32 /// dream: don't forget your dreams! /// ====================================== #include "spellcorrectserver.h" #include "configuration.h" #include "cachemanger.h" #include "timerthread.h" #include <iostream> #include <functional> using namespace std; using namespace wd; int main() { wd::cachemanger *mycachemanger=singleton<cachemanger>::getinstance(singleton<configuration> ::getinstance(confpath)->getcache()); timerthread timer(5,600,std::bind(&cachemanger::periodicupdate,mycachemanger)); timer.start(); spellcorrectserver myspell(singleton<configuration>::getinstance(confpath)->getip() ,singleton<configuration>::getinstance(confpath)->getport() ,4 ,10); myspell.start(); return 0; }
小结:(1)已经实现项目需求,中文和英文单词都能查询,经过测试,运行稳定,能输出不少候选词
(2)仍然存在少量bug,例如偶尔会发生段错误
(3)由于时间问题,json读出的数据key-value的key值没有打印,用户界面还未来得及优化
(4)陈硕的《linux多线程服务端编程》使用linux接口(timerfd),没用posix接口(eventfd)