个人主页Milestone-里程碑❄️个人专栏: 力扣hot100 CLinux心向往之行必能至目录一.Dict.hpp二.InetAddr.hpp三.udpclient.cc四.udpserver.hpp五.udpserver.cc六.总结上一篇的文章中,我们说过可以通过function进行回调函数,使客户端输入数据,服务端可以处理,本篇将基于该原理,实现英文查中文大致与上篇差不多,只不过多封装了一个类,用来进行加载与回调函数,实现查找一.Dict.hpp#pragma once #include iostream #include fstream #include string #include unordered_map #include Log.hpp #includeInetAddr.hpp using namespace LogModule; const std::string gap :; const std::string defaultdictpath ./word.txt; class Dict { public: Dict() : _dict_path(defaultdictpath) { } bool LoadDict() { std::ifstream in(_dict_path); if (!in.is_open()) { LOG(LogLevel::DEBUG) 打开字典: _dict_path 错误; return false; } std::string line; while (std::getline(in, line)) { auto pos line.find(gap); if (pos std::string ::npos) { LOG(LogLevel::WARNING) 解析: line 失败; continue; } // 左闭右开 std::string word line.substr(0, pos); std::string chinaese line.substr(pos gap.size()); if (word.empty() || chinaese.empty()) { LOG(LogLevel::WARNING) 没有有效数据 line; continue; } _dict.insert(std::make_pair(word, chinaese)); LOG(LogLevel::DEBUG) 加载: line; } in.close(); return true; } std::string Translate(const std::string word, InetAddr client) { auto iter _dict.find(word); if (iter _dict.end()) { // LOG(LogLevel::DEBUG) word ; LOG(LogLevel::DEBUG) 进入到了翻译模块, [ client.IP() : client.PORT() ]# word -None; return None; } return iter-second; } ~Dict() {} private: std::string _dict_path; // 默认路径 std::unordered_mapstd::string, std::string _dict; };二.InetAddr.hpp调用系统接口实现两种形式的转换#pragma once #include iostream class InetAddr { public: // InetAddr(){} InetAddr(sockaddr_in addr) : _addr(addr) { _port ntohs(_addr.sin_port); _ip inet_ntoa(_addr.sin_addr); //// 4字节网络风格的IP - 点分十进制的字符串风格的IP } std::string IP() { return _ip ;} uint16_t PORT() { return _port; } ~InetAddr() {} private: struct sockaddr_in _addr; std::string _ip; uint16_t _port; };三.udpclient.cc#include iostream #include string #include cstring #include netinet/in.h #include arpa/inet.h #include sys/types.h #include sys/socket.h #includeLog.hpp using namespace LogModule; int main(int args,char *argv[]) { if(args!3) { std::cerr Usage: argv[0] server_ip server_port std::endl; return 1; } // 1. 创建socket int sockfdsocket(AF_INET,SOCK_DGRAM,0); if(sockfd0) { std::cerrsocket failstd::endl; return 2; } // 2. 本地的ip和端口是什么要不要和上面的“文件”关联呢 // 问题client要不要bind需要bind. // client要不要显式的bind?不要首次发送消息OS会自动给client进行bindOS知道IP端口号采用随机端口号的方式 // 为什么一个端口号只能被一个进程bind为了避免client端口冲突 // client端的端口号是几不重要只要是唯一的就行 // 填写服务器信息 std::string server_ipargv[1]; std::uint16_t server_portstd::stoi(argv[2]);//转数字 struct sockaddr_in server; memset(server,0,sizeof(server)); server.sin_familyAF_INET; server.sin_port htons(server_port); server.sin_addr.s_addrinet_addr(server_ip.c_str()); while(true) { //接受信息 std::string input; std::coutPLEASE Enter# ; std::getline(std::cin,input); int nsendto(sockfd,input.c_str(),input.size(),0,(struct sockaddr*)server,sizeof(server)); (void)n; char buffer[1024]; struct sockaddr_in peer; socklen_t lensizeof(peer); int mrecvfrom(sockfd,buffer,sizeof(buffer),0,(struct sockaddr*)peer,len); if(m0) { buffer[m]0; std::coutbufferstd::endl; } } return 0; }四.udpserver.hpp#pragma once #include iostream #include string #include functional #include strings.h #include sys/types.h #include sys/socket.h #include netinet/in.h #include arpa/inet.h #include functional #include Log.hpp #include InetAddr.hpp // #include InetAddr.hpp using namespace LogModule; using func_t std::functionstd::string(const std::string , InetAddr ); int defaultsockfd -1; class UdpServer { public: UdpServer(uint16_t port, func_t func) : _sockfd(defaultsockfd), _func(func), _port(port), _isrunning(false) { } void Init() { // 1. 创建套接字 _sockfd socket(AF_INET, SOCK_DGRAM, 0); if (_sockfd 0) { LOG(LogLevel::FATAL) socket0 error!; exit(1); } LOG(LogLevel::INFO) socket success,sockfd: _sockfd; // 2. 绑定socket信息ip和端口 ip(比较特殊后续解释) // 2.1 填充sockaddr_in结构体 struct sockaddr_in local; bzero(local, sizeof(local)); // 初始化 // 我会不会把我的IP地址和端口号发送给对方 // IP信息和端口信息一定要发送到网络 // 本地格式-网络序列 local.sin_family AF_INET; local.sin_port htons(_port); // local.sin_addr.s_addr inet_addr(_ip.c_str()); // TODO local.sin_addr.s_addr INADDR_ANY; // IP也是如此1. IP转成4字节 2. 4字节转成网络序列 - in_addr_t inet_addr(const char *cp); // 那么为什么服务器端要显式的bind呢IP和端口必须是众所周知且不能轻易改变的 // 此时只开在了栈上,要通过系统调用写入内核 int n bind(_sockfd, (struct sockaddr *)local, sizeof(local)); if (n -1) { perror(bind:); LOG(LogLevel::FATAL) bind fail; exit(2); } LOG(LogLevel::INFO) bind success!; } void Start() { _isrunning true; while (_isrunning) { char buffer[1024]; struct sockaddr_in peer; socklen_t len sizeof(peer); // 1. 收消息, client为什么要个服务器发送消息啊不就是让服务端处理数据。 ssize_t n recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)peer, len); if (n 0) { buffer[n] 0; InetAddr client(peer); // LOG(LogLevel::DEBUG) [ peer_ip : peer_port ]# buffer; // 1. 消息内容 2. 谁发的 // // 2. 发消息 // std::string echo_string server echo#; // echo_string buffer; // 收到单词 // std::string buffer_str(buffer); std::string result _func(buffer, client); LOG(LogLevel::INFO)翻译结果buffer -result.c_str(); // std::string result _func(buffer,client); sendto(_sockfd, result.c_str(), result.size(), 0, (struct sockaddr *)peer, sizeof(peer)); } else { LOG(LogLevel::FATAL) recvfrom fail; exit(1); } } } ~UdpServer() {} private: int _sockfd; uint16_t _port; // std::string _ip; // 用的是字符串风格点分十进制, 192.168.1.1 func_t _func; bool _isrunning; };五.udpserver.cc#include iostream #include memory #include functional #include UdpServer.hpp #include Dict.hpp std::string defaulthandler(const std::string message) { std::string hello hello, ; hello message; return hello; } int main(int argc, char *argv[]) { if (argc ! 2) { std::cerr Usage: argv[0] port std::endl; return 1; } // std ::string ip argv[1]; uint16_t port std::stoi(argv[1]); // xiecuo Enable_Console_Log_Strategy(); Dict dict; dict.LoadDict(); // std::unique_ptrUdpServer usvr std::make_uniqueUdpServer(port,defaulthandler); std::unique_ptrUdpServer usvr std::make_uniqueUdpServer(port, [dict](const std::string word, InetAddr cli) - std::string { return dict.Translate(word, cli); }); usvr-Init(); usvr-Start(); return 0; }六.总结1.实现查找功能,应该熟悉哈希 stl等的相关接口2.为了使代码更加清晰,我们可以再创建一个文件和一个类,专门来获取addr port ip等信息3.同时别忘了给每个客服端发送消息带上个人信息(防止多人在线,不清楚是谁发送)
40 UDP - 2 C++实现英汉词典查询服务
个人主页Milestone-里程碑❄️个人专栏: 力扣hot100 CLinux心向往之行必能至目录一.Dict.hpp二.InetAddr.hpp三.udpclient.cc四.udpserver.hpp五.udpserver.cc六.总结上一篇的文章中,我们说过可以通过function进行回调函数,使客户端输入数据,服务端可以处理,本篇将基于该原理,实现英文查中文大致与上篇差不多,只不过多封装了一个类,用来进行加载与回调函数,实现查找一.Dict.hpp#pragma once #include iostream #include fstream #include string #include unordered_map #include Log.hpp #includeInetAddr.hpp using namespace LogModule; const std::string gap :; const std::string defaultdictpath ./word.txt; class Dict { public: Dict() : _dict_path(defaultdictpath) { } bool LoadDict() { std::ifstream in(_dict_path); if (!in.is_open()) { LOG(LogLevel::DEBUG) 打开字典: _dict_path 错误; return false; } std::string line; while (std::getline(in, line)) { auto pos line.find(gap); if (pos std::string ::npos) { LOG(LogLevel::WARNING) 解析: line 失败; continue; } // 左闭右开 std::string word line.substr(0, pos); std::string chinaese line.substr(pos gap.size()); if (word.empty() || chinaese.empty()) { LOG(LogLevel::WARNING) 没有有效数据 line; continue; } _dict.insert(std::make_pair(word, chinaese)); LOG(LogLevel::DEBUG) 加载: line; } in.close(); return true; } std::string Translate(const std::string word, InetAddr client) { auto iter _dict.find(word); if (iter _dict.end()) { // LOG(LogLevel::DEBUG) word ; LOG(LogLevel::DEBUG) 进入到了翻译模块, [ client.IP() : client.PORT() ]# word -None; return None; } return iter-second; } ~Dict() {} private: std::string _dict_path; // 默认路径 std::unordered_mapstd::string, std::string _dict; };二.InetAddr.hpp调用系统接口实现两种形式的转换#pragma once #include iostream class InetAddr { public: // InetAddr(){} InetAddr(sockaddr_in addr) : _addr(addr) { _port ntohs(_addr.sin_port); _ip inet_ntoa(_addr.sin_addr); //// 4字节网络风格的IP - 点分十进制的字符串风格的IP } std::string IP() { return _ip ;} uint16_t PORT() { return _port; } ~InetAddr() {} private: struct sockaddr_in _addr; std::string _ip; uint16_t _port; };三.udpclient.cc#include iostream #include string #include cstring #include netinet/in.h #include arpa/inet.h #include sys/types.h #include sys/socket.h #includeLog.hpp using namespace LogModule; int main(int args,char *argv[]) { if(args!3) { std::cerr Usage: argv[0] server_ip server_port std::endl; return 1; } // 1. 创建socket int sockfdsocket(AF_INET,SOCK_DGRAM,0); if(sockfd0) { std::cerrsocket failstd::endl; return 2; } // 2. 本地的ip和端口是什么要不要和上面的“文件”关联呢 // 问题client要不要bind需要bind. // client要不要显式的bind?不要首次发送消息OS会自动给client进行bindOS知道IP端口号采用随机端口号的方式 // 为什么一个端口号只能被一个进程bind为了避免client端口冲突 // client端的端口号是几不重要只要是唯一的就行 // 填写服务器信息 std::string server_ipargv[1]; std::uint16_t server_portstd::stoi(argv[2]);//转数字 struct sockaddr_in server; memset(server,0,sizeof(server)); server.sin_familyAF_INET; server.sin_port htons(server_port); server.sin_addr.s_addrinet_addr(server_ip.c_str()); while(true) { //接受信息 std::string input; std::coutPLEASE Enter# ; std::getline(std::cin,input); int nsendto(sockfd,input.c_str(),input.size(),0,(struct sockaddr*)server,sizeof(server)); (void)n; char buffer[1024]; struct sockaddr_in peer; socklen_t lensizeof(peer); int mrecvfrom(sockfd,buffer,sizeof(buffer),0,(struct sockaddr*)peer,len); if(m0) { buffer[m]0; std::coutbufferstd::endl; } } return 0; }四.udpserver.hpp#pragma once #include iostream #include string #include functional #include strings.h #include sys/types.h #include sys/socket.h #include netinet/in.h #include arpa/inet.h #include functional #include Log.hpp #include InetAddr.hpp // #include InetAddr.hpp using namespace LogModule; using func_t std::functionstd::string(const std::string , InetAddr ); int defaultsockfd -1; class UdpServer { public: UdpServer(uint16_t port, func_t func) : _sockfd(defaultsockfd), _func(func), _port(port), _isrunning(false) { } void Init() { // 1. 创建套接字 _sockfd socket(AF_INET, SOCK_DGRAM, 0); if (_sockfd 0) { LOG(LogLevel::FATAL) socket0 error!; exit(1); } LOG(LogLevel::INFO) socket success,sockfd: _sockfd; // 2. 绑定socket信息ip和端口 ip(比较特殊后续解释) // 2.1 填充sockaddr_in结构体 struct sockaddr_in local; bzero(local, sizeof(local)); // 初始化 // 我会不会把我的IP地址和端口号发送给对方 // IP信息和端口信息一定要发送到网络 // 本地格式-网络序列 local.sin_family AF_INET; local.sin_port htons(_port); // local.sin_addr.s_addr inet_addr(_ip.c_str()); // TODO local.sin_addr.s_addr INADDR_ANY; // IP也是如此1. IP转成4字节 2. 4字节转成网络序列 - in_addr_t inet_addr(const char *cp); // 那么为什么服务器端要显式的bind呢IP和端口必须是众所周知且不能轻易改变的 // 此时只开在了栈上,要通过系统调用写入内核 int n bind(_sockfd, (struct sockaddr *)local, sizeof(local)); if (n -1) { perror(bind:); LOG(LogLevel::FATAL) bind fail; exit(2); } LOG(LogLevel::INFO) bind success!; } void Start() { _isrunning true; while (_isrunning) { char buffer[1024]; struct sockaddr_in peer; socklen_t len sizeof(peer); // 1. 收消息, client为什么要个服务器发送消息啊不就是让服务端处理数据。 ssize_t n recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)peer, len); if (n 0) { buffer[n] 0; InetAddr client(peer); // LOG(LogLevel::DEBUG) [ peer_ip : peer_port ]# buffer; // 1. 消息内容 2. 谁发的 // // 2. 发消息 // std::string echo_string server echo#; // echo_string buffer; // 收到单词 // std::string buffer_str(buffer); std::string result _func(buffer, client); LOG(LogLevel::INFO)翻译结果buffer -result.c_str(); // std::string result _func(buffer,client); sendto(_sockfd, result.c_str(), result.size(), 0, (struct sockaddr *)peer, sizeof(peer)); } else { LOG(LogLevel::FATAL) recvfrom fail; exit(1); } } } ~UdpServer() {} private: int _sockfd; uint16_t _port; // std::string _ip; // 用的是字符串风格点分十进制, 192.168.1.1 func_t _func; bool _isrunning; };五.udpserver.cc#include iostream #include memory #include functional #include UdpServer.hpp #include Dict.hpp std::string defaulthandler(const std::string message) { std::string hello hello, ; hello message; return hello; } int main(int argc, char *argv[]) { if (argc ! 2) { std::cerr Usage: argv[0] port std::endl; return 1; } // std ::string ip argv[1]; uint16_t port std::stoi(argv[1]); // xiecuo Enable_Console_Log_Strategy(); Dict dict; dict.LoadDict(); // std::unique_ptrUdpServer usvr std::make_uniqueUdpServer(port,defaulthandler); std::unique_ptrUdpServer usvr std::make_uniqueUdpServer(port, [dict](const std::string word, InetAddr cli) - std::string { return dict.Translate(word, cli); }); usvr-Init(); usvr-Start(); return 0; }六.总结1.实现查找功能,应该熟悉哈希 stl等的相关接口2.为了使代码更加清晰,我们可以再创建一个文件和一个类,专门来获取addr port ip等信息3.同时别忘了给每个客服端发送消息带上个人信息(防止多人在线,不清楚是谁发送)