无法通过RESP协议在Redis服务器执行命令问题求助
你现在碰到的情况很典型:直接用.NET控制台的Socket给Redis发命令,不管是普通字符串还是按RESP协议构造的格式,字节数据明明发出去了,但Redis那边完全没反应,命令根本没生效。结合你给出的代码片段,我整理几个核心排查点和解决办法:
1. 先确认Socket连接是否真的建立,且数据发送完整
首先,你代码里的Socket sock = new Socket(Add...是不是完整初始化了Redis的IP和端口,并且调用了ConnectAsync(或者同步的Connect)?如果连接没成功建立,发数据肯定是石沉大海。
另外,Socket的Send方法不一定会一次性发送所有字节,它的返回值是实际发送的字节数,所以必须循环发送直到全部字节都发出去:
byte[] commandBytes = Encoding.ASCII.GetBytes(command1); int bytesSent = 0; while (bytesSent < commandBytes.Length) { bytesSent += sock.Send(commandBytes, bytesSent, commandBytes.Length - bytesSent, SocketFlags.None); }
如果是用异步方法SendAsync,也要确保等待发送完成,或者用循环处理未发送完的字节。
2. 检查命令格式的正确性
普通字符串命令的问题
你定义的command0 = "set a 200"少了命令结尾的\r\n!Redis的TCP协议要求每个命令必须以CRLF(\r\n)结尾,否则Redis会一直等待命令输入完成,自然不会执行。所以正确的普通命令应该是:
public static string command0 = "set a 200\r\n";
RESP协议格式的小细节
你构造的command1看起来格式是对的,但要再确认两个点:
- 所有换行必须是
\r\n,不能是单个\n; - 每个参数的长度必须和实际字符数一致(比如
$3对应后面的set,长度确实是3,这个没问题)。
另外,编码用Encoding.ASCII是对的,Redis命令都是ASCII字符,不会有乱码问题。
3. 一定要读取Redis的响应
很多人会忽略这一点:Redis在收到并处理命令后会返回响应,如果你只发送不读取,可能会导致TCP缓冲区积压,甚至Redis会认为连接有问题。更重要的是,读取响应能直接告诉你命令是否执行成功,比如:
byte[] responseBuffer = new byte[1024]; int bytesReceived = sock.Receive(responseBuffer); string response = Encoding.ASCII.GetString(responseBuffer, 0, bytesReceived); Console.WriteLine("Redis响应:" + response);
如果返回+OK\r\n,说明命令执行成功;如果是-ERR ...开头的响应,就能直接看到错误原因(比如认证失败、语法错误等)。
4. 排查Redis服务器的配置
- 确认Redis是否允许你的客户端IP连接:检查Redis配置文件里的
bind选项,如果设置了特定IP,你的客户端IP不在列表里就会被拒绝; - 有没有开启密码认证?如果Redis设置了
requirepass,你需要先发送AUTH yourpassword\r\n(或者RESP格式的AUTH命令)才能执行其他命令; - 保护模式是否开启?默认情况下,Redis的保护模式会拒绝非本地的连接,如果你是远程连接Redis,需要关闭保护模式(
protected-mode no)或者设置密码。
完整的示例代码
这里给你一个能正常运行的完整示例,包含连接、发送命令、读取响应的流程:
using System; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading.Tasks; class Program { // 修正后的普通命令(加了\r\n结尾) public static string command0 = "set a 200\r\n"; // 正确的RESP格式命令 public static string command1 = "*3\r\n$3\r\nset\r\n$1\r\na\r\n:200\r\n"; static void Main(string[] args) { Task.Run(async () => await SendRedisCommand()).Wait(); } static async Task SendRedisCommand() { // 替换成你的Redis服务器IP和端口 var redisEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 6379); using (Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) { try { // 建立连接 await sock.ConnectAsync(redisEndPoint); Console.WriteLine("已成功连接到Redis服务器"); // 选择发送RESP格式的命令 byte[] commandBytes = Encoding.ASCII.GetBytes(command1); // 确保全部字节发送完成 int bytesSent = 0; while (bytesSent < commandBytes.Length) { bytesSent += await sock.SendAsync( new ArraySegment<byte>(commandBytes, bytesSent, commandBytes.Length - bytesSent), SocketFlags.None); } Console.WriteLine("命令已发送"); // 读取Redis响应 byte[] responseBuffer = new byte[1024]; int bytesReceived = await sock.ReceiveAsync(new ArraySegment<byte>(responseBuffer), SocketFlags.None); string response = Encoding.ASCII.GetString(responseBuffer, 0, bytesReceived); Console.WriteLine($"Redis响应:{response}"); // 发送get命令验证结果 byte[] getCommand = Encoding.ASCII.GetBytes("*2\r\n$3\r\nget\r\n$1\r\na\r\n"); bytesSent = 0; while (bytesSent < getCommand.Length) { bytesSent += await sock.SendAsync( new ArraySegment<byte>(getCommand, bytesSent, getCommand.Length - bytesSent), SocketFlags.None); } bytesReceived = await sock.ReceiveAsync(new ArraySegment<byte>(responseBuffer), SocketFlags.None); response = Encoding.ASCII.GetString(responseBuffer, 0, bytesReceived); Console.WriteLine($"执行get a的结果:{response}"); } catch (Exception ex) { Console.WriteLine($"执行出错:{ex.Message}"); } finally { // 关闭连接 sock.Shutdown(SocketShutdown.Both); sock.Close(); } } } }
按照上面的步骤排查,应该就能解决命令不生效的问题了。
内容的提问来源于stack exchange,提问作者Bercovici Adrian

