Linux下C/C++ redis协议(RESP)解析

Redis是一个开源的内存键值数据存储,最常用作主数据库、缓存、消息代理和队列。Redis提供了亚毫秒的响应时间,在游戏、金融科技、广告技术、社交媒体、医疗保健和物联网等行业实现了快速而强大的实时应用。

Redis连续五年成为开发人员最喜爱的数据库。开发人员喜欢Redis,因为它的易用性、性能和可扩展性。Redis客户端可用于各种流行的现代编程语言。再加上性能优势,Redis成为缓存、会话管理、游戏、欺诈检测、排行榜、实时分析、地理空间索引、拼车、社交媒体和流媒体应用程序最受欢迎的选择。

使用CLI探索Redis

外部程序使用TCP套接字和Redis特定的协议与Redis进行通信。该协议在Redis客户端库中实现,用于不同的编程语言。然而,为了简化Redis的黑客攻击,Redis提供了一个命令行实用程序,可以用来向Redis发送命令。这个程序叫做redis-cli。

这里我们先看看redis服务器是否开启。
要检查Redis是否正常工作,首先要做的是使用reds-cli发送一个PING命令:

运行reds-cli,后跟一个命令名及其参数,将此命令发送到本地主机6379端口上运行的redis实例。可以更改reds-cli使用的主机和端口,只需尝试–help选项即可检查使用情况信息。

另一种运行redis-cli的方法是不带参数:程序将以交互模式启动。您可以键入不同的命令并查看它们的答复。

保护好你的Redis

默认情况下,Redis绑定到所有接口,并且根本没有身份验证。如果你在一个非常可控的环境中使用Redis,与外部互联网分离,通常与攻击者分离,那没关系。

然而,如果一个未经保护的Redis暴露在互联网上,这将是一个巨大的安全问题。如果您不能100%确定您的环境是否正确安全,请检查以下步骤以使Redis更安全,这些步骤是为了提高安全性而登记的。

1.确保Redis用于监听连接的端口(默认情况下为6379,如果您在集群模式下运行Redis,则为16379,Sentinel为26379)已防火墙,因此无法从外部联系Redis。

2.使用设置了bind指令的配置文件,以确保Redis只侦听您正在使用的网络接口。例如,如果您只是从同一台计算机本地访问Redis,则仅使用环回接口(127.0.0.1),依此类推。

3.使用requirepass选项可以添加额外的安全层,以便客户端需要使用AUTH命令进行身份验证。

4.如果您的环境需要加密,请使用spiped或其他SSL隧道软件来加密Redis服务器和Redis客户端之间的流量。

请注意,在没有任何安全性的情况下暴露在互联网上的Redis实例很容易被利用,所以请确保您理解以上内容,并至少应用一个防火墙层。防火墙就位后,尝试从外部主机连接reds-cli,以证明该实例实际上是不可访问的。

从应用程序(hiRedis)中使用Redis

当然,仅仅从命令行界面使用Redis是不够的,因为目标是从应用程序中使用它。为了做到这一点,您需要下载并安装适用于您的编程语言的Redis客户端库。

hiRedis使用方法一般顺序为先用 redisConnect 连接数据库,然后用 redisCommand 执行命令,执行完后用 freeReplyObject 来释放redisReply对象,最后用 redisFree 来释放整个连接。

...
int main(int argc, char **argv) 
{
    
    /* 连接到redis */
		...

    redisContext *c = redisConnect(hostname,port);
    
    struct timeval timeout = { 1, 500000 }; // 1.5 seconds
    if (isunix) 
	{
        c = redisConnectUnixWithTimeout(hostname, timeout);
    } 
	else 
	{
        c = redisConnectWithTimeout(hostname, port, timeout);
    }
    if (c == NULL || c->err) 
	{
        if (c) 
		{
            printf("Connection error: %s\n", c->errstr);
            redisFree(c);
        } 
		else 
		{
            printf("Connection error: can't allocate redis context\n");
        }
        exit(1);
    }
    printf("Connected to redis\n");

    // redisFree(c);

    /* 2 - PING server */
    redisReply *reply;  /* 临时答复指针 */
    reply = redisCommand(c,"PING");
    printf("PING: %s\n", reply->str);

    /* 3 - Set a key */
    reply = redisCommand(c,"SET %s %s", "foo", "hello world");
    printf("SET %s %s \t| %s\n", "foo", "hello world", reply->str);
    freeReplyObject(reply);     // 释放回复对象

    /* 3 - Get a key */
    reply = redisCommand(c,"GET %s","foo");
    printf("GET %s \t\t| ","foo");
    printf("%s\n",reply->str);
    freeReplyObject(reply);

    /* Set a key using binary safe API */
    // reply = redisCommand(c,"SET %b %b", "bar", (size_t) 3, "hello", (size_t) 5);
    // printf("SET (binary API): %s\n", reply->str);
    // freeReplyObject(reply);
    redisFree(c);

    return 0;
}

运行结果:

我们也可以使用 nc 命令来替代 redis-cli 命令行:

什么是 RESP?

Redis 的客户端和服务端之间采取了一种独立名为 RESP(REdis Serialization Protocol) 的协议。通过 tcp流式套接字来进行通讯,为了 防止粘包 因此命令或数据均以 \r\n (CRLF) 结尾,然后根据解析规则解析相应信息。

RESP协议可以序列化多种类型,比如Simple Strings(简单字符串),Errors(错误类型),Integers(整形),Bulk Strings(批量串)和Arrays(数组),但此协议只适用于Redis客户端-服务端之间的通信,Redis集群中节点间通信使用的另一种协议。

RESP协议说明

RESP协议是在Redis 1.2中引入的,但它成为了与Redis 2.0中的Redis服务器通信的标准方式。这是所有Redis客户端都要遵循的协议,我们甚至可以基于此协议,开发实现自己的Redis客户端。

RESP在Redis中用作请求-响应协议的方式如下:

1.客户端将命令作为Bulk Strings的RESP数组发送到Redis服务器。
2.服务器根据命令实现回复一种RESP类型。
3.在RESP中,某些数据的类型取决于第一个字节:
    +代表简单字符串(Simple Strings)比如OK,PONG(对应客户端的PING命令)
    -代表错误类型(Errors)
    :代表整型(Integers)
    $代表多行字符串(Bulk Strings)
    *代表数组(Arrays)
此外,RESP能够使用稍后指定的Bulk Strings或Array的特殊变体来表示Null值。
在RESP中,协议的不同部分始终以“\r\n”(CRLF)结束。

RESP抓包验证

我们知道,Redis客户端与server端通信,本身就是基于tcp的一个Request/Response模式。我们不妨用网络抓包工具,拦截客户端与server端传输的数据、一探究竟。抓包使用tcpdump命令,具体参数这里就不多说了,使用的命令是:

tcpdump host 127.0.0.1 and port 6379 -i lo -w redis-packet-test.cap

抓取的结果保存在redis-packet-test.cap,分析工具使用Wireshark,在分析之前,先说下客户端与服务端交互的命令:

1.info,返回redis服务端的相关信息
2.set abc 111,服务端响应OK
3.get abc,返回111
4.lpush abclist 1 2 3,返回 9
5.ee,这是个错误命令,主要看下服务端返回的错误数据格式
接下来我们结合数据包分析下:

redis-cli:

TCP三次握手建立连接的

PSH是发送数据,ACK是响应.
在这里插入图片描述

首先发送的命令是info,先看右边部分,可以看到一开始是*1:表示长度为1的数组,后边的··对应左边是0d 0a,其实就是\r\n的16进制表示形式,然后后边$4:代表长度为4的Bulk Strings,也就是info,后边紧跟着info。
info命令返回数据包:$1924:长度为1924的Bulk Strings,后边便是服务器相关信息

set abc 111命令:*3:长度为3的数组,后边是数组里的3个元素:$3··set(长度为3的Bulk Strings)、$3··xfh、$3··111
在这里插入图片描述返回数据:+OK(代表简单字符串‘OK’)简单字符串一般是服务器状态相关,比如’OK’、‘PONG’等;而Bulk Strings可以包含任何内容(比如换行符、控制符)

命令lpush abclist 1 2 3:看到这相信大家都已经明白了,*5代表长度为5的数组,后边紧跟着5个Bulk Strings lpush、xfhlist、1、2、3,而且每个元素的前边都有长度,分别是$5、$7、$1、$1、$1
在这里插入图片描述返回::9(表示整形数据9)

命令ff,这是个错误命令,redis中没有这个命令,应该返回语法错误


返回:发现前缀是-,对应RESP协议中的错误类型,后边紧跟着ERR unknown command ‘ff’


Linux下C/C++ RESP协议解析

...
int query_parser(const u_char* pkt_data, unsigned int data_len, char **query)
{
...
}
int resp_parser(const u_char* pkt_data, unsigned int data_len, char **resp)
{
...
}
...
void packetHandle(u_char* arg, const struct pcap_pkthdr* header, const u_char* pkt_data)
{
...
    if ( !pkt_data )
	{
        printf ("Didn't grab packet!/n");
        exit (1);
    }
    if (header->caplen < header->len) return;
    pehdr = (struct ether_header*)pkt_data;
    pkt_data += *linkhdrlen;
    
    piphdr = (struct ip*)pkt_data;
    pkt_data += IP_HL(piphdr);
    data_len = ntohs(piphdr->ip_len) - IP_HL(piphdr);
    switch(piphdr->ip_p)
	{
        case IPPROTO_TCP:
            ptcphdr = (struct tcphdr*)pkt_data;
            data_len = data_len - TCP_OFF(ptcphdr);
            pkt_data += TCP_OFF(ptcphdr);
            strcpy(sip, inet_ntoa(piphdr->ip_src));
            strcpy(dip, inet_ntoa(piphdr->ip_dst));
            sport = ntohs(ptcphdr->source);
            dport = ntohs(ptcphdr->dest);
            break;
        default:
            data_len = 0;
            pkt_data = NULL;
            break;
    }
    if (data_len == 0 || pkt_data == NULL ) return;

...

    signal(SIGINT, bailout);
    signal(SIGTERM, bailout);
    signal(SIGQUIT, bailout);
}   

...
int main(int argc, char **argv)
{
...

    while ((i = getopt(argc, argv, "hi:p:")) != -1) 
	{
        switch(i)
		{
            case 'h':
                Usage();
                return -1;
                break;
            case 'i':
                option.device = optarg;
                break;
            case 'p':
                option.port = atoi(optarg);
                break;
            default:
                break;
        }
    }

    sprintf(option.bufstr, "port %d", option.port);

    char *d = getenv("jdebug");
    if ( d != NULL &&  !strcmp(d, "true")) 
        debug = 1;
    
    dbg("debug mode\n");

    if((pHandle = init_pcap_t(option.device, option.bufstr)))
	{
        sniff_loop(pHandle, (pcap_handler)packetHandle);
    }    
...

}

运行结果:

If you need the complete source code, please add the WeChat number (c17865354792)

总结

Redis 基于 RESP (Redis Serialization Protocal)协议来完成客户端和服务端通讯的。RESP 本质是一种文本协议,实现简单、易于解析。底层采用的是TCP的连接方式,通过tcp进行数据传输,然后根据解析规则解析相应信息。

Welcome to follow WeChat official account【程序猿编码
参考:https://redis.io/docs/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/6001.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

从小到大排序-课后程序(JavaScript前端开发案例教程-黑马程序员编著-第4章-课后作业)

【案例4-1】 从小到大排序 一、案例描述 考核知识点 函数的定义与调用&#xff0c;参数传递 练习目标 掌握函数的定义与调用。掌握for循环数据处理逻辑 需求分析 给出一组数据&#xff0c;要求按照从小到大进行排序。 案例分析 效果如图4-1所示。从小到大排序 具体实现步骤…

Final Cut Pro for Mac(中文fcpx视频剪辑)

Final Cut Pro for Mac是一款专业的视频剪辑软件&#xff0c;由苹果公司开发并发布。Final Cut Pro for Mac v10.6.5中文版是最新版本&#xff0c;支持中文界面&#xff0c;为用户提供了更加便捷的操作体验。 使用Final Cut Pro for Mac&#xff0c;用户可以轻松地进行视频编辑…

哈希表(如何打造一个工业级的哈希表)

目录 哈希思想 哈希函数 哈希冲突 1.开放寻址法 2、链表法 解决装载因子过大的问题 选择合适的哈希冲突解决方法 哈希思想 哈希表(hashtable)是数组的一一种扩展&#xff0c;由数组演化而来&#xff0c;底层依赖数组支持按下标快速 访问元素的特性。换句话说&#xff0c;…

HTTP协议详解(二)

目录 1.HTTP 响应详解 1.1认识状态码(status code) 1.2 认识响应报头(header) 1.3 认识响应正文(body) 2.构造 HTTP 请求 2.1 通过form表单构造请求 2.2 通过ajax构造请求 2.3 使用第三方工具构造请求 开始之前我们先复习一下http协议格式 1.HTTP 响应详解 我们先抓包…

ChatGPT中文方式写作-chatgpt中文生成

ChatGPT是一种强大的自然语言处理技术&#xff0c;可以帮助人们进行各种语言任务&#xff0c;包括机器翻译、问答系统、自然语言生成等。在中文辅助写作上&#xff0c;ChatGPT也很有用武之地&#xff0c;下面我们将就如何通过ChatGPT实现中文辅助写作&#xff0c;提高文章质量和…

C语言预处理指令-宏定义、文件包含、条件编译

预处理指令简介 1.C语言在对源程序进行编译之前&#xff0c;会先对一些特殊的预处理指令作解释(比如之前使用的#include文件包含指令)&#xff0c;产生一个新的源程序(这个过程称为编译预处理),之后再进行通常的编译 2.为了区分预处理指令和一般的C语句&#xff0c;所有预处理…

计算机科班与培训开发编程的区别在哪里?

科班、培训班、科班培训班的模式都培养了很多编程技术人员进入IT行业&#xff0c;有的成为某个技术领域的专家&#xff0c;有的成为领导层&#xff0c;有的一直在默默无闻的敲代码等待35岁的到来。不管那种方式入行&#xff0c;这些类似的情况都存在&#xff0c;并且未来还会一…

全链路监控:方案概述

问题背景 随着微服务架构的流行&#xff0c;服务按照不同的维度进行拆分&#xff0c;一次请求往往需要涉及到多个服务。互联网应用构建在不同的软件模块集上&#xff0c;这些软件模块&#xff0c;有可能是由不同的团队开发、可能使用不同的编程语言来实现、有可能布在了几千台…

java版工程项目管理系统-功能清单 图文解析

ava版工程项目管理系统 Spring CloudSpring BootMybatisVueElementUI前后端分离 功能清单如下&#xff1a; 首页 工作台&#xff1a;待办工作、消息通知、预警信息&#xff0c;点击可进入相应的列表 项目进度图表&#xff1a;选择&#xff08;总体或单个&#xff09;项目显示1…

哈希表(概念,冲突的解决,实现哈希桶)

目录 概念 冲突 如何尽量减少冲突? 负载因子 解决冲突的几种方案 冲突严重时的解决办法 哈希表的实现 基本类型哈希桶实现 泛型哈希桶实现 注意!!! 概念 构造出一种存储结构,通过某种函数使元素的存储位置(下标)与它的关键码之间能够建立一一的映射关系,那么在查找…

让ChatGpt可以看视频,看文档,帮你总结,并提供示例的github项目(附体验地址)

github地址&#xff1a;https://github.com/madawei2699/myGPTReader 演示 Stay updated with the latest news summaries daily with chatGPT. Use chatGPT to read and provide a summary of any webpage include the video(YouTube). 总之这个玩意有很多&#xff0c;可以…

【K8S系列】深入解析StatefulSet(一)

序言 那些看似不起波澜的日复一日&#xff0c;一定会在某一天让你看见坚持的意义。 文章标记颜色说明&#xff1a; 黄色&#xff1a;重要标题红色&#xff1a;用来标记结论绿色&#xff1a;用来标记一级论点蓝色&#xff1a;用来标记二级论点Kubernetes (k8s) 是一个容器编排平…

java毕业生就业信息管理系统servlet程序

1&#xff0e;系统登录&#xff1a;系统登录是用户访问系统的路口&#xff0c;设计了系统登录界面&#xff0c;包括用户名、密码和验证码&#xff0c;然后对登录进来的用户判断身份信息&#xff0c;判断是管理员用户还是普通用户。 2&#xff0e;系统用户管理&#xff1a;不管是…

第13章_约束

第13章_约束 &#x1f3e0;个人主页&#xff1a;shark-Gao &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是shark-Gao&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f389;目前状况&#xff1a;23届毕业生&#xff0c;目前在某公司…

一次压测遇到的问题和排查过程记录

文章目录问题 & 排查1、cpu使用率过高问题描述问题排查解决方案扩展内容2、504 Gateway Time-out问题描述问题排查解决方案3、限流对压测的影响问题描述问题排查解决方案jmeter相关1、beanShell 动态生成签名2、响应断言3、导出结果树请求和响应文件问题 & 排查 1、cp…

域名批量查询功能常用查询方法教程

一些用户在抱怨&#xff0c;要找到好域名怎么就那么不容易呢&#xff0c;能不能让我批量查下不含0的数字啊&#xff0c;能不能查下不含4的数字啊&#xff0c;能不能查下AABBB这样的域名啊…… 别着急&#xff0c;这就给您支招啦&#xff1a;通过西部数码强大的批量查询功能&am…

活动报名|SOFA 五周年,Live Long and Prosper!

2018 年 4 月 19 日&#xff0c;我们在北京启程&#xff0c;伴随种下希望的种子&#xff0c;举办了 SOFAStack 社区的第一个开放日。转眼来到 2023 年&#xff0c;瑞兔送福又逢春暖花开&#xff0c;怀揣着新的愿景&#xff0c;我们将于 4 月 15 日回到北京庆祝 SOFA 的五岁生日…

服务器磁盘又双叒叕爆满了?被/proc占满?

又双叒叕服务器前言排查分析前言 继上一次文章&#xff1a; MISCONF Redis is configured to save RDB snapshots, but it is currently not able to persist on disk. 通过删除tomcat下的catalina.out文件&#xff0c;解决了磁盘爆满的问题后。今天又双叒叕出现这个问题。。…

万字长文解读「新一代CMDB落地的困境及出路」

2023年3月21日春分时节&#xff0c;优维结合在CMDB技术领域的经验沉淀与洞察能力&#xff0c;梳理金融客户在数据运营中面临的问题和挑战&#xff0c;为了帮助到广大客户建立健全有效的方法参考&#xff0c;全新策划了一档“CMDB数据运营精准化专场公开课”线上直播课程。该系列…

2023-Python实现百度翻译接口调用

目录 &#x1f449;1、目标网址 ​​​​​​​&#x1f449;2、接口分析调试 ​​​​​​​&#x1f449;3、python 代码实现 学习记录&#xff1a;百度翻译 ​​​​​​​&#x1f449;1、目标网址 百度翻译&#xff1a;百度翻译-200种语言互译、沟通全世界&#xff0…