文章目录
- 使用方法
- 输入
- 输出解释
- 代码
- 输出
使用方法
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);
}
}