自制C++连接redis
干什么的?
使用纯socket实现C++连接redis服务器的一套接口
c++
#include <cstdio>
#include <fcntl.h>
#include <iostream>
#include <string>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
class RedisClient{
public:
RedisClient();
// 连接到Redis服务器
bool Connect(const std::string& host, short port);
// 身份认证
bool Auth(const std::string& user_name,const std::string& password = "");
bool Set(const std::string& key,const std::string& value);
std::string Get(const std::string& key);
bool Pub(const std::string& channel,const std::string& message);
//只能订阅一个频道
bool Sub(const std::string& channel);
//用于同步接收redis-server的任何数据 然后打印到控制台
bool SyncRead();
void Close();
private:
void ReadFlag();
bool SyncWrite( std::string commend);
//$
std::string ReadString();
//-
std::string ReadError();
//+
std::string ReadOK();
//:
int ReadNum();
//*
void ReadBatch();
private:
std::queue<std::string> sub_queue; //订阅消息队列
bool is_recv_sub; //判断是否正在接受订阅数据
int client_socket;
char flag; //消息标志
};c++
#include "RedisClient.h"
#include <cstdio>
#include <iostream>
#include <string>
#include <sys/socket.h>
#include <thread>
#include <unistd.h>
RedisClient::RedisClient(){
is_recv_sub = false;
}
bool RedisClient::Connect(const std::string& host, short port){
// 创建套接字
client_socket = socket(AF_INET, SOCK_STREAM, 0);
if (client_socket == -1) {
std::cerr << "Failed to create socket\n";
return false;
}
// 连接到Redis服务器
struct sockaddr_in server_address;
server_address.sin_family = AF_INET;
server_address.sin_port = htons(port); // Redis默认端口为6379
server_address.sin_addr.s_addr = inet_addr(host.c_str()); // Redis服务器地址
if (connect(client_socket, (struct sockaddr *)&server_address, sizeof(server_address)) == -1) {
std::cerr << "Connection to Redis server failed\n";
return false;
}
return true;
}
// 身份认证
bool RedisClient::Auth(const std::string& user_name,const std::string& password){
SyncWrite("AUTH "+user_name+" "+password);
ReadFlag();
if(flag == '+'){
ReadOK();
return true;
}
ReadError();
return false;
}
bool RedisClient::Set(const std::string& key,const std::string& value){
SyncWrite("SET "+key+" "+value);
ReadFlag();
if(flag == '-'){
std::cerr<< ReadError();
return false;
}
ReadOK();
return true;
}
std::string RedisClient::Get(const std::string& key){
SyncWrite("GET "+key);
ReadFlag();
if(flag == '-'){
return ReadError();
}
return ReadString();
}
bool RedisClient::Pub(const std::string& channel,const std::string& message){
std::string commend = "PUBLISH "+channel+" "+message+"\r\n";
SyncWrite(commend);
SyncRead();
}
bool RedisClient::Sub(const std::string& channel){
std::string commend = "SUBSCRIBE "+channel+"\r\n";
SyncWrite(commend);
//读掉+
ReadFlag();
ReadOK();
ReadFlag();
ReadNum();
ReadFlag();
ReadString();
ReadFlag();
ReadString();
ReadFlag();
ReadNum();
is_recv_sub = true;
// sub_thread = std::thread([=](){
while (is_recv_sub) {
ReadFlag();
//读掉*后面的数字
ReadNum();
//读掉 message 和 channel名
ReadFlag();
ReadString();
//读掉 channel名
ReadFlag();
ReadString();
//读消息
ReadFlag();
std::cout<<"msg: "<<ReadString()<<"\n";
}
// });
// sub_thread.join();
}
inline void RedisClient::ReadFlag(){
recv(client_socket, &flag, 1, MSG_WAITALL);
// MSG_OOB:用于接收带外数据(Out-of-Band data)。
// MSG_PEEK:允许从接收队列中查看数据,但不会将数据从接收队列中移除。
// MSG_WAITALL:要求接收函数等待,直到请求的字节数都被接收到。
// MSG_DONTWAIT:类似于将socket设置为非阻塞模式,使recv函数立即返回,不会阻塞等待数据到达。
// MSG_TRUNC:如果接收缓冲区不足以容纳接收的数据,将截断数据,并且不会报错。
// 使用MSG_WAITALL时 fd必须处于阻塞模式下(默认是阻塞) 否则不起作用
//
}
bool RedisClient::SyncWrite(std::string commend){
commend += "\r\n";
if (send(client_socket, commend.c_str(), commend.length(), 0) == -1) {
std::cerr << "Failed to send authentication request\n";
close(client_socket);
return false;
}
return true;
}
//$
inline std::string RedisClient::ReadString(){
int len = ReadNum();
//如果是-1 说明没有这个键值对
if(len != -1){
char response[len];
if (recv(client_socket, &response, len, MSG_WAITALL) == -1) {
std::cerr << "Failed to receive response\n";
return "(nil)";
}
// std::cout << "ReadString: " << std::string(response).substr(0,len) << "\n";
std::string ret = std::string(response).substr(0,len);
//读掉\r\n
recv(client_socket, &response, 2, 0);
return ret;
}
return "(nil)";
}
//-
inline std::string RedisClient::ReadError(){
std::string str;
while (true) {
char c;
if(recv(client_socket, &c, 1, 0) == -1){
std::cerr <<"Failed to receive response\n";
return "(error)";
}
if(c == '\r'){
continue;
}
if(c == '\n'){
break;
}
str += c;
}
return str;
}
//+
inline std::string RedisClient::ReadOK() {
char response[4];
int bytes_received = recv(client_socket, response, sizeof(response), 0);
if (bytes_received == -1) {
std::cerr << "Failed to receive response\n";
return "";
}
return response;
}
//:
inline int RedisClient::ReadNum(){
std::string num_str;
while (true) {
char c;
if (recv(client_socket, &c, 1, 0) == -1) {
std::cerr << "Failed to receive response\n";
close(client_socket);
return -1;
}
if (c == '\r') {
continue;
}
if (c == '\n') {
break;
}
num_str += c;
}
// int num = std::stoi(num_str);
return std::stoi(num_str);
// std::cout << "ReadNum: " << num << "\n";
}
//*
inline void RedisClient::ReadBatch(){
int num = ReadNum();
std::cout << "*个数: " << num << "\n";
for(int i = 0;i < num;i++){
SyncRead();
}
}
inline bool RedisClient::SyncRead(){
//先读一个字节看看是什么类型
ReadFlag();
switch (flag) {
case '+': // +OK\r\n
std::cout<<"ReadOK(): "<<ReadOK()<<"\n";
break;
case '-': // -ERR ______________\r\n
std::cout<<"ReadError(): "<<ReadError()<<"\n";
break;
case '$': // $5\r\n12345\r\n
std::cout<<"ReadString(): "<<ReadString()<<"\n";
break;
case '*': // *3\r\n$5\r\n12345\r\n$1\r\n5\r\n$2\r\n22\r\n
std::cout<<"---------------\n";
ReadBatch();
break;
case ':': // :1000
std::cout<<"ReadNum(): "<<ReadNum()<<"\n";
break;
default:
std::cerr << "Unexpected response from server\n";
break;
}
}
void RedisClient::Close(){
close(client_socket);
}基本使用
c++
#include <RedisClient.h>
int main() {
RedisClient redis_client;
redis_client.Connect("127.0.0.1", 6389);
redis_client.Auth("root","");
std::cout<<redis_client.Set("a","1246456")<<"\n";
std::cout<<redis_client.Get("a")<<"\n";
redis_client.Close();
return 0;
}