Help needed for QQ0

I’m stuck on Stage #QQ0

I am trying to run this program on CLion using WSL with Ubuntu 22 distro

What i am seeing is that when I send the command via telnet

*2\r\n$4\r\nECHO\r\n$9\r\nraspberry\r\n

C++ is inetrpretting \r\n as 4 characters.

this logic works fine in this way

if (buffer[position] != ‘\’ && buffer[position+1] != ‘r’ && buffer[position+2] != ‘\’ && buffer[position+3] != ‘n’)

But when I run it on code crafters \r is considered one char. Any idea how to fix it

Here are my logs:

Client connected
buffer[0]: *
buffer[1]: 2
buffer[2]: \
buffer[3]: r
buffer[4]: \
buffer[5]: n
buffer[6]: $
buffer[7]: 4
buffer[8]: \
buffer[9]: r
buffer[10]: \
buffer[11]: n
buffer[12]: E
buffer[13]: C
buffer[14]: H
buffer[15]: O
buffer[16]: \
buffer[17]: r
buffer[18]: \
buffer[19]: n
buffer[20]: $
buffer[21]: 9
buffer[22]: \
buffer[23]: r
buffer[24]: \
buffer[25]: n
buffer[26]: r
buffer[27]: a
buffer[28]: s
buffer[29]: p
buffer[30]: b
buffer[31]: e
buffer[32]: r
buffer[33]: r
buffer[34]: y
buffer[35]: \
buffer[36]: r
buffer[37]: \
buffer[38]: n

And here’s a snippet of my code:

#include <iostream>
#include <cstdlib>
#include <string>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <thread>
#include <vector>

using namespace std;

vector<string> processRESPCommand(string &buffer);
string processArray(vector<string> &command);

void handleRequest(int clientSocket)
{

  string readBuffer;

  while (true)
  {
    char buffer[1024];

    memset(buffer, 0, sizeof(buffer));

    int recv_bytes = recv(clientSocket, buffer, 1024, 0);

    if (recv_bytes == 0)
    {
      std::cout << "Client disconnected\n";
      close(clientSocket);
      return;
    }
  for (int i = 0; i < 40; i++) {
    cout<<"buffer["<<i<<"]: "<<buffer[i]<<"\n";
  }
    readBuffer=buffer;

   vector<string> command = processRESPCommand(readBuffer);
    cout<<command[0]<<"\n";
    const char *message = processArray(command).c_str();
    send(clientSocket, message, strlen(message), 0);
  }
}

int main(int argc, char **argv)
{
  // Flush after every std::cout / std::cerr
  std::cout << std::unitbuf;
  std::cerr << std::unitbuf;

  // You can use print statements as follows for debugging, they'll be visible when running tests.
  std::cout << "Logs from your program will appear here!\n";

  // test1();

  // Uncomment this block to pass the first stage

  int server_fd = socket(AF_INET, SOCK_STREAM, 0);
  if (server_fd < 0)
  {
    std::cerr << "Failed to create server socket\n";
    return 1;
  }

  // Since the tester restarts your program quite often, setting SO_REUSEADDR
  // ensures that we don't run into 'Address already in use' errors
  int reuse = 1;
  if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
  {
    std::cerr << "setsockopt failed\n";
    return 1;
  }
  //
  struct sockaddr_in server_addr;
  server_addr.sin_family = AF_INET;
  server_addr.sin_addr.s_addr = INADDR_ANY;
  server_addr.sin_port = htons(6379);
  //
  if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) != 0)
  {
    std::cerr << "Failed to bind to port 6379\n";
    return 1;
  }
  //
  int connection_backlog = 5;
  if (listen(server_fd, connection_backlog) != 0)
  {
    std::cerr << "listen failed\n";
    return 1;
  }

  struct sockaddr_in client_addr;
  int client_addr_len = sizeof(client_addr);

  std::cout << "Waiting for a client to connect...\n";

  while (true)
  {

    int clientSocket = accept(server_fd, (struct sockaddr *)&client_addr, (socklen_t *)&client_addr_len);
    std::cout << "Client connected\n";
    thread t(handleRequest, clientSocket);

    t.detach();
  }

  close(server_fd);

  return 0;
}

vector<string> processRESPCommand(string &buffer)
{
string local_buffer=buffer;
  cout<<local_buffer<<"\n";
  int position = 0;
  vector<string> command;
  vector<string> noValidCommand={"NVC"};

  int sizeOfbuffer = buffer.size();

  if (sizeOfbuffer < 1)
  {
    return noValidCommand;
  }

  if (buffer[position] != '*')
  {
    return {"nvc1"};
  }

  position++;
  int commandLength = stoi(&buffer[position]);

  position++;


  if (buffer[position] != '\\' && buffer[position+1] != 'r' && buffer[position+2] != '\\' && buffer[position+3] != 'n')
  {
    cout<<"output: "<<buffer[position]<<buffer[position+1]<<buffer[position+2]<<buffer[position+3]<<"\n";
    return {"nvc2"};
  }

  position += 4;

  for (int i = 0; i < commandLength; i++)
  {

    if (buffer[position] != '$')
    {
      return {"nvc3"};
    }

    position++;
    int tokenLength = stoi(&buffer[position]);

    position++;

    if (buffer[position] != '\\' && buffer[position+1] != 'r' && buffer[position+2] != '\\' && buffer[position+3] != 'n')
    {
      return {"nvc4"};
    }

    position += 4;

    command.push_back(buffer.substr(position, tokenLength));
    position += tokenLength;
    vector<string> gg;
    gg.push_back(to_string(buffer.size()));

    if (buffer[position] != '\\' && buffer[position+1] != 'r' && buffer[position+2] != '\\' && buffer[position+3] != 'n')
    {
      return command;
    }

    position += 4;


  }

  cout<<"Command1: "<<command[0]<<endl;
  return command;
 
}

string processArray(vector<string> &command) {

    if (command[0]=="PING") {
      return "$4\r\nPONG\r\n";
    }

  if (command[0]=="ECHO") {

    return "$"+to_string(command[1].size())+"\r\n"+command[1]+"\r\n";;
  }

}


Looks like I figured out. telnet woould send every things as chaarcter and the socket is interpreting as individual chaarcters

1 Like

Hey @manish112, glad to hear you figured it out!

Would you like any help with handling \n as an escaped character? I’m not sure how to do that myself, but we can try figuring it out together!