文章目录
- 使用方法
- 输入
- 输出解释
- 代码
- 输出
使用方法
g++ main.cpp
./a.out -h 127.0.0.1 -p 6379
输入
一行内输入 redis 命令
keys*
set name
get name
等等 redis命令
输出解释
输入redis: redis收到的redis协议的数据
human输入: 你输入的原始字符
redis输出: redis返回的基于 redis协议的数据
human输出: 稍微处理过的 redis放回的数据
代码
#include <iostream>
#include <string>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <vector>
#include <unistd.h>
#include <sstream>
#include <getopt.h>
using namespace std;string ip = "127.0.0.1";
int p = 6379;class TcpCli {
public:string ip;int port;int fd;explicit TcpCli(const string& ip = "127.0.0.1", int port = 80) {this->ip = ip;this->port = port;fd = -1;}~TcpCli() {if (fd != -1) {close(fd);}}bool connect() {// 电话号码fd = socket(AF_INET, SOCK_STREAM, 0);if (fd == -1) {return false;}// 拨号sockaddr_in saddr{};saddr.sin_family = AF_INET;saddr.sin_port = htons(port);saddr.sin_addr.s_addr = inet_addr(ip.c_str());int ret = ::connect(fd, (sockaddr *) &saddr, sizeof(saddr));if (ret == -1) {return false;}return true;}int send(const string& data) {return ::send(fd, data.c_str(), data.size(), 0);}int recv(string& data) {char buf[1024];int ret = ::recv(fd, buf, sizeof(buf), 0);if (ret > 0) {data.assign(buf, ret);}return ret;}};class RedisRequestProtocol {
public:static string makeRequest(const vector<string> &args) {string res = "*" + to_string(args.size()) + "\r\n";res += "$" + to_string(args[0].size()) + "\r\n";res += args[0] + "\r\n";for (int i = 1; i < args.size(); i++) {res += "$" + to_string(args[i].size()) + "\r\n";res += args[i] + "\r\n";}return res;};public:static string set(const string &key, const string &value) {return makeRequest({"set", key, value});}static string get(string key) {return makeRequest({"get", key});}
};string beautify(const string& data) {string res;string res2;for (int i = 0; i < data.size(); i++) {if (data[i] != '\r' && data[i] != '\n') {res += data[i];res2 += data[i];continue;}if (data[i] == '\r') {res += "\\r\\n";i++;}}return res;
}string deleteSome(const string& data, char ch) {string res;for (int i = 0; i < data.size(); i++) {if (data[i] == ch) {int pos = data.find("\r\n", i);i = pos + 1;} else {res += data[i];}}return res;
}string human(const string& data) {if (data.empty()) {return data;}if (data[0] == '-') {return data.c_str() + 1;}string res;// 删掉多行字符串标记string temp = deleteSome(data, '$');// 删去数组标记temp = deleteSome(temp, '*');return res = temp;
}void tishi(const string& addr, string op = "输入redis:") {cout << op << addr;
}class RedisCli {
public:TcpCli cli;string addr;RedisRequestProtocol requestProtocol;RedisCli(string ip = "127.0.0.1", int port = 6379) : cli(ip, port) {addr = ip + ":" + to_string(port);if (!cli.connect()) {cout << "connect error" << endl;exit(-1);}}template<class ...Args>void runCommand(const Args... args) {string request = requestProtocol.makeRequest({args...});tishi(addr + "> ");cout << beautify(request) << endl;auto ret = cli.send(request);if (ret == -1) {throw "send error";}string data;ret = cli.recv(data);if (ret == -1) {throw "recv error";}tishi(addr + "> ", "redis输出:");cout << beautify(data)<< endl;tishi(addr + ">\n", "human输出:");cout << human(data);}
};void parseArgv(int argc, char** argv) {int option;while((option=getopt(argc, argv, "h:p:"))) {switch(option) {case 'h':ip = optarg;break;case 'p':p = atoi(optarg);break;default:return;}}
}int main(int argc, char** argv) {parseArgv(argc, argv);RedisCli cli(ip, p);string command;cout << "*********************为了看的清楚, 我们把返回数据的\\r\\n打出来了, 并分行显示*********************" << endl;while (true) {tishi(cli.addr + "> ", "human输入:");if (!getline(cin, command) || command.empty()) {continue;}if (command == "q" || command == "Q") {break;}stringstream ss(command);vector<string> args;string arg;while(ss >> arg) {args.push_back(arg);}cli.runCommand(args);}}