Skip to content

自制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;
}

学 习 记 录