目录
翻译单词版
引入
除了echo消息,也可以让其作为一个翻译软件
- 翻译不就是在我们搜单词时才会提供服务吗
- 刚好符合我们的短时服务的要求
思路
我们仍然采用多线程的模式
只需要变动一下task.hpp(也就是处理数据的方式),以及增加一个单词文本文件和读取文件
- 每次翻译软件启动时,都需要读取文件放入内存中,让客户端在线查询中英文的的切换
代码
init.hpp
cpp#include <fstream> #include <unordered_map> #define file_name "./word.txt" #define delimiter ":" void segmentation(std::string &info, std::string &part1, std::string &part2) { auto it = info.find(delimiter); part1 = info.substr(0, it); // 左闭右开 part2 = info.substr(it + 1, info.size()); } struct words_init { words_init() { std::ifstream in(file_name); std::string info; while (std::getline(in, info)) //从文件中读取,初始化words_ { std::string part1, part2; segmentation(info, part1, part2); words_[part1] = part2; } } std::string translation(const std::string &info) { auto it = words_.find(info);//单词集里查找是否有输入的单词 std::string res; if (it == words_.end()) { return "UnKnown"; } else { return it->second; //翻译 } return nullptr; } std::unordered_map<std::string, std::string> words_; };
task.hpp
cpp#pragma once #include <iostream> #include <string> #include <stdio.h> #include "helper.hpp" #include "init.hpp" // 这里的任务是,服务端在收到客户端的连接后的后续工作 words_init w; class Task { public: Task() {} // 方便只是为了接收传参而定义一个对象 Task(int fd, const char ip[32], const uint16_t port) : sockfd_(fd), ip_(ip), port_(port) { } void operator()() { function(); } private: void function() { char buffer[buff_size]; memset(buffer, 0, sizeof(buffer)); while (true) { int n = read(sockfd_, buffer, sizeof(buffer) - 1); if (n < 0) { lg(ERROR, "%s:%d read error, %s", ip_.c_str(), port_, strerror(errno)); break; } else if (n == 0) { lg(INFO, "%s:%d quit", ip_.c_str(), port_); break; } else { buffer[n] = 0; // std::string res = process_info(buffer, ip_, port_); std::string res = w.translation(buffer); write(sockfd_, res.c_str(), res.size()); } } } private: int sockfd_; uint16_t port_; std::string ip_; };
运行情况
成功哩:
多个客户端也可以:
添加重连功能
引入
当我们的服务端关闭后,客户端想要向服务端写入时,客户端会被迫退出:
- 因为套接字在写失败时,会给写入的一端发送sigpipe信号:
- 但在cs模式中,我们并不希望这样
- 因为某些问题导致的服务端断开连接/客户端这边出了某些问题,而服务端很可能会快速恢复/客户端重新连接服务端可能就会恢复(比如)
- 所以我们更希望客户端能在发生错误时,进行一些处理(比如重连),而不是进程直接退出
- 就像我们玩的各种游戏,在网络波动的时候,都会有转圈圈啦,显示断线重连中啦,都是因为他们有进行错误处理
- 所以,我们除了设置read错误时的处理,也需要处理sigpipe的信号处理方式(忽略/自定义)
思路
所以我们考虑设置重连措施
- 设定重连次数(不然客户端会一直重连)
- 每次重连都需要重新创建套接字
- 因为一旦断开连接,当前套接字的连接状态无法恢复,不能简单地通过connect重新连接
- 只能创建新的套接字,然后通过这个新的套接字建立新的连接(客户端和服务端都是这样):
- 像是下图这样,在服务端是通过accept函数创建用于io的套接字
代码
cppvoid short_service() { signal(SIGPIPE, SIG_IGN); // 忽略这个信号,以便我们进行重连 struct sockaddr_in *server_addr = new sockaddr_in; memset(server_addr, 0, sizeof(*server_addr)); server_addr->sin_family = AF_INET; inet_pton(AF_INET, ip_.c_str(), &(server_addr->sin_addr)); server_addr->sin_port = htons(port_); while (true) // 为了让io失败后,可以进入重连功能 { bool is_reconnect = false; int re_num = 5; do { sockfd_ = socket(AF_INET, SOCK_STREAM, 0); if (sockfd_ < 0) { exit(SOCK_ERROR); } int ret = connect(sockfd_, reinterpret_cast<struct sockaddr *>(server_addr), sizeof(*server_addr)); if (ret < 0) { is_reconnect = true; --re_num; std::cout << "im reconnect ing..." << std::endl; sleep(2); } else { std::cout << "connect success !" << std::endl; is_reconnect = false; } } while (is_reconnect && re_num); if (re_num == 0) { break; // 重连次数达到限制,客户端退出 } std::cout << "please enter:" << std::endl; std::string buffer; std::getline(std::cin, buffer); int n = write(sockfd_, buffer.c_str(), buffer.size()); if (n < 0) { continue; // 除了处理读失败,写失败也需要(向不存在的文件写入) } char info[1024]; memset(info, 0, sizeof(info)); n = read(sockfd_, info, sizeof(info) - 1); if (n > 0) { info[n] = 0; std::cout << info << std::endl; } else { continue; } close(sockfd_); } }
运行情况