在我正在制作的项目的这个阶段,我需要添加对 ECHO 命令的支持。
ECHO 是一个类似于 PING 的命令,用于测试和调试。它接受单个参数并将其作为 RESP 批量字符串返回。
$ redis-cli PING # The command I implemented in previous stages
PONG
$ redis-cli ECHO hey # The command I will implement in this stage
hey
测试人员将执行这样的程序:
$ ./your_program.sh
然后它会向您的服务器发送带有参数的 ECHO 命令:
$ redis-cli ECHO hey
测试人员期望收到
$3\\r\\nhey\\r\\n
作为响应(这是编码为 RESP 批量字符串的字符串。
import socket
import threading
def handle_client(conn, addr):
print(f'Connected to {addr}')
while True:
data = conn.recv(1024) # Read data as bytes
if not data: # If no data is received, exit the loop
break
request = data.decode()
pong = b"+PONG\r\n"
if request.lower().startswith("echo"):
# Parse the message for the ECHO command
parts = request.split("\r\n")
if len(parts) >= 4:
res_data = parts[3] # Extract the actual argument for ECHO (the string after ECHO)
content_len = len(res_data) # Get the length of the argument
# Correctly format the response as a bulk string
response = f"${content_len}\r\n{res_data}\r\n"
conn.send(response.encode()) # Send the response back
else:
# Default response for PING
conn.send(b"+PONG\r\n")
print(f"Connection to {addr} closed")
conn.close() # Close the connection with the client
def main():
print("Logs from your program will appear here!")
# Set up the server socket and start listening for connections
server_socket = socket.create_server(("localhost", 6379), reuse_port=True)
server_socket.listen()
while True:
# Accept a new client connection
conn, addr = server_socket.accept()
# Create and start a new thread to handle the client
client_thread = threading.Thread(target=handle_client, args=(conn, addr))
client_thread.start()
if __name__ == "__main__":
main()
Initiating test run...
⚡ This is a turbo test run. https://codecrafters.io/turbo
Running tests. Logs should appear shortly...
[compile] Moved ./.codecrafters/run.sh → ./your_program.sh
[compile] Compilation successful.
[tester::#QQ0] Running tests for Stage #QQ0 (Implement the ECHO command)
[tester::#QQ0] $ ./your_program.sh
[your_program] Logs from your program will appear here!
[tester::#QQ0] $ redis-cli ECHO pineapple
[your_program] Connected to ('127.0.0.1', 39346)
[tester::#QQ0] Expected "pineapple", got "PONG"
[tester::#QQ0] Test failed (try setting 'debug: true' in your codecrafters.yml to see more details)
我认为解析器无法读取 ECHO 命令,但我不知道如何修复它。任何帮助将不胜感激。
为每个命令定义一个函数,然后在第一个空格字符上分割输入数据,并为该命令调用适当的函数,将剩余的数据传递给该函数。
def pong(data, conn):
conn.send(b"+PONG\r\n")
def echo(data, conn):
conn.send(f"${len(data)}\r\n{data}\r\n".encode())
def handle_client(conn, addr):
print(f'Connected to {addr}')
commands = {
"PING": pong,
"ECHO": echo,
}
while True:
data = conn.recv(1024) # Read data as bytes
if not data: # If no data is received, exit the loop
break
name, _, extra_data = data.decode().rstrip().partition(" ")
command = commands.get(name.upper(), None)
if command:
command(extra_data, conn)
print(f"Connection to {addr} closed")
conn.close() # Close the connection with the client
以下是问题的答案:
import threading
def parse_resp(data):
"""Parse RESP format data."""
data = data.decode()
if not data:
return None, None
# Handle array format (*2\r\n$4\r\nECHO\r\n$6\r\norange\r\n)
if data[0] == '*':
# Split into lines
parts = data.split('\r\n')
# First line contains number of arguments (*2)
num_args = int(parts[0][1:])
args = []
i = 1
for _ in range(num_args):
# Skip the length indicator ($4)
str_len = int(parts[i][1:])
# Get the actual string value
args.append(parts[i + 1])
i += 2
return args[0], ' '.join(args[1:]) if len(args) > 1 else ''
return None, None
def pong(data, conn):
conn.send(b"+PONG\r\n")
def echo(data, conn):
# Format response in RESP format for bulk strings
response = f"${len(data)}\r\n{data}\r\n"
conn.send(response.encode())
def handle_client(conn, addr):
print(f'Connected to {addr}')
commands = {
"ECHO": echo,
"PING": pong,
}
while True:
try:
data = conn.recv(1024) # Read data as bytes
if not data: # If no data is received, exit the loop
break
command, args = parse_resp(data)
if command:
command_handler = commands.get(command.upper())
if command_handler:
command_handler(args, conn)
except Exception as e:
print(f"Error handling client: {e}")
break
print(f"Connection to {addr} closed")
conn.close()
def main():
print("Logs from your program will appear here!")
server_socket = socket.create_server(("localhost", 6379), reuse_port=True)
server_socket.listen()
while True:
conn, addr = server_socket.accept()
client_thread = threading.Thread(target=handle_client, args=(conn, addr))
client_thread.start()
if __name__ == "__main__":
main()