目录
前言
上一篇文章中,我们使用UDP协议编码完成了一个简单的服务器,实现数据通信,服务器设计出来后目的不仅仅只是实现数据通信,而是根据客户端发过来的请求,实现一定的需求,今天我们要介绍的是当客户端给服务端发送英文单词,然后服务端获取客户端的请求,将翻译结果返回给客户端,通过这样的方式,实现了一款英文翻译服务器。下面我们就一起具体来看看是如何编码完成。
1.设计思路:
如图所示
第一步:启动服务器,然后服务器加载词库
第二步:客户端向服务器,发送请求
第三步:服务器处理请求查找单词,将查找结果返回给客户端
第四步:客户端获取查询结果
2.词库设计
说明:在这里只是简单模拟实现一个词库,主要是实现业务逻辑
dict.txt:
aunt:姨母brother:兄弟cousin:堂兄弟couple:夫妇dad:爸爸daughter:女儿family:家father:爸爸grandchild:孙子granddaughger:孙女grandfather:祖父grandma:外婆grandpa:外公granny老奶奶
3.设计客户端
udpClient.hpp
#pragma once#include #include #include #include #include #include #include #include #include #include #include #include namespace Client{ using namespace std; class udpClient { public: udpClient(const string &serverIp, const uint16_t serverPort) : _serverIp(serverIp), _serverPort(serverPort), _sockfd(-1) {} void initClient() { _sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (_sockfd == -1) { cerr << "socket error:" << errno << strerror(errno) << endl; exit(2); } } void run() { struct sockaddr_in server; memset(&server, 0, sizeof(server)); server.sin_family = AF_INET; server.sin_addr.s_addr = inet_addr(_serverIp.c_str()); server.sin_port = htons(_serverPort); while (1) { string message; cout << "请输入你想要翻译的单词:"; getline(cin,message); //发送请求 sendto(_sockfd, message.c_str(), message.size(), 0, (const struct sockaddr *)&server, sizeof(server)); char buffer[1024]; struct sockaddr_in temp; socklen_t len = sizeof(temp); //接受查询翻译结果 size_t n = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&temp, &len); if (n >= 0) buffer[n] = 0; cout << "翻译的结果为: " << buffer << endl; } } private: string _serverIp; int _sockfd; uint16_t _serverPort; };}
udpClient.cc:启动客户端
#include"udpClient.hpp"#includeusing namespace Client;static void Usage(string proc){ cout << "\nUsage:\n\t" << proc << " server_ip server_port\n\n";}int main(int argc,char* argv[]){ if(argc != 3) { Usage(argv[0]); exit(1); } string serverip = argv[1]; uint16_t serverport = atoi(argv[2]); unique_ptr uct(new udpClient(serverip,serverport)); uct->initClient(); uct->run(); return 0;}
4.设计服务端
udpServer.hpp
#pragma once#include #include #include #include #include #include #include #include #include #include #include #include namespace Server{ using namespace std; const static string defaultIP = "0.0.0.0"; enum {USAGE_ERR = 1, SOCKET_ERR, BIND_ERR,OPEN_ERR}; typedef function func_t; class udpServer { public: udpServer(const func_t& cb,uint16_t port, const string &ip = defaultIP) :_callback(cb),_port(port),_ip(ip),_sockfd(-1) {} void initServer() { _sockfd = socket(AF_INET,SOCK_DGRAM,0); if(_sockfd == -1) { cerr<<"socket error:" << errno << strerror(errno) << endl; exit(SOCKET_ERR); } struct sockaddr_in local; bzero(&local,sizeof(local)); local.sin_family = AF_INET; local.sin_port = htons(_port); local.sin_addr.s_addr = htonl(INADDR_ANY); int n = bind(_sockfd,(struct sockaddr*)&local,sizeof(local)); if(n == -1) { cerr<<"bind error:" << errno << strerror(errno) << endl; exit(BIND_ERR); } } void startServer() { char buffer[1024]; for(;;) { struct sockaddr_in peer; socklen_t len = sizeof(peer); ssize_t s = recvfrom(_sockfd,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&peer,&len); if(s) { buffer[s] = { 0 }; string clientIp = inet_ntoa(peer.sin_addr); uint16_t clientPort = ntohs(peer.sin_port); string message = buffer; cout << clientIp << "[" << clientPort << "]" << message << endl; //服务器只负责接受数据,处理方法采用回调的方式交给上层处理 _callback(_sockfd,clientIp,clientPort,message); } } } ~udpServer() {} private: uint16_t _port; string _ip; int _sockfd; func_t _callback; };}
udpServer.cc:启动服务端
#include "udpServer.hpp"#include #include #include using namespace Server;static void Usage(string proc){ cout << "\nUsage:\n\t" << proc << " local_port\n\n";}const string DictTxt = "./dict.txt";unordered_map dict;static bool cutString(string& str,string& s1,string& s2,const string& sep){ auto pos = str.find(sep); if(pos == string::npos) return false; s1 = str.substr(0,pos); s2 = str.substr(pos + sep.size()); return true;}static void initDict(){ ifstream in(DictTxt,ios::binary); if(!in.is_open()) { cerr << "open fail:" << DictTxt << "error" << endl; exit(OPEN_ERR); } string line; string key,value; while(getline(in,line)) { if(cutString(line,key,value,":")) { dict.insert(make_pair(key,value)); } } in.close(); cout << "load dict success" << endl;}//翻译:void TranslationWord(int sockfd,string clientIp,uint16_t clientPort,string message){ string response_message; auto iter = dict.find(message); if(iter == dict.end()) response_message = "unknown"; else response_message = iter->second; struct sockaddr_in client; bzero(&client, sizeof(client)); client.sin_family = AF_INET; client.sin_port = htons(clientPort); client.sin_addr.s_addr = inet_addr(clientIp.c_str()); sendto(sockfd, response_message.c_str(), response_message.size(), 0, (struct sockaddr*)&client, sizeof(client));}int main(int argc, char *argv[]){ if (argc != 2) { Usage(argv[0]); exit(USAGE_ERR); } //加载词库 initDict(); uint16_t port = atoi(argv[1]); unique_ptr usvr(new udpServer(TranslationWord,port)); usvr->initServer(); usvr->startServer(); return 0;}
5.编译客户端和服务端
makefile:
.PHONY:allall:udpServer udpClientudpServer:udpServer.ccg++ -o $@ $^ -std=c++11udpClient:udpClient.ccg++ -o $@ $^ -std=c++11.PHONY:cleanclean:rm -f udpServer udpClient
6.测试结果
如图所示:服务端能够准确处理客户端的请求,将翻译查询结果返回给客户端
7.总结
以上就是使用UDP协议实现的一款翻译服务器,细心的小伙伴也已经发现了,在上面的代码中服务器的任务只是接受请求,然后将请求的数据回调处理,让上层处理业务逻辑,这样的实现方式实现了服务器与业务逻辑代码之间的解耦,如果以后想实现一款别的需求的服务器,只需要更改上层的业务逻辑就可以了。
来源地址:https://blog.csdn.net/qq_65307907/article/details/132164347