C: обработать `cd ..’ и другие команды в серверном приложении и вернуть обратную связь клиенту

#c #linux #server #client-server

#c #linux #сервер #клиент-сервер

Вопрос:

Итак, у меня запущено серверное / клиентское приложение. Пока я могу довольно хорошо общаться с сервером. Я могу передать его в ls команду process, чтобы клиент получил список файлов. Здесь я наткнулся на проблему, когда я хотел бы ввести на сервер различные команды, такие как cd .. , mv , cp или cd directory чтобы сервер мог их обработать, а клиент получил отзыв об этих действиях. Я думал, что код будет где-то между этими строками:

 message_size = recv(socket_fd, input, sizeof(input)/sizeof(input[0]), 0);
if(message_size > 0) {
  /* Process incoming message, add end of the line sign */
  input[message_size] = '';

  /* Process command and get a response from it */
  fp = popen(input, "r");
  if(fp == NULL)
    fprintf(stderr, "popen() failed: %sn", strerror(errno));
  else {

    // IS THIS THE RIGHT APPROACH??
    if(strcmp(input, "ls") == 0) {
      while((c = getc(fp)) != EOF) {
        output[i] = c;
        i  ;
      }
    i = 0;
    send_message(socket_fd, output);
   }
   else if(strcmp(input, "cd ..") == 0) {
      send_message(socket_fd, "cd ..");
   }
 }

 pclose(fp);
  

Краткое объяснение: input это то, что сервер получает от клиента. Это может быть cd .. . send_message это функция просто для отправки некоторой информации клиенту — ничего страшного. fp является ли тип FILE *

Как я могу различать различные команды, получать от них отзывы и особенно — получать отзывы о плохо вставленной команде от клиента asdf . Я думал, что с помощью strcmp I можно выполнять различные обработки на клиенте, но по какой-то причине мой клиент зависает при вводе ls .

Клиентский код:

 #include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <netinet/in.h>
#include <string.h>
#include <pthread.h>
#include <signal.h>

#define MAX_SIZE 10000

int main(int argc, char* argv[])
{
  int socket_fd, port_number;
  struct sockaddr_in serv_addr;
  struct hostent *server;
  char buffer[MAX_SIZE];
  int rc;

  if(argc<2) {
    fprintf(stderr,"Incorrect arguments inputn");
    exit(0);
  }

  port_number = 12345;

  server = gethostbyname(argv[1]);

  socket_fd = socket(AF_INET, SOCK_STREAM, 0);
  if(socket_fd < 0) {
    perror("Error opening socket");
    exit(1);
  }

  bzero((char*) amp;serv_addr, sizeof(serv_addr));
  serv_addr.sin_family = AF_INET;
  serv_addr.sin_port = htons(port_number);
  bcopy((char*) server->h_addr,(char *) amp;serv_addr.sin_addr.s_addr,sizeof(server->h_length));

  if(connect(socket_fd, (struct sockaddr *)amp;serv_addr, sizeof(serv_addr)) < 0) {
      perror("Error connecting");
      exit(1);
  }

  enum {
    INIT_STATE,
    READY_STATE
  } serv_state = INIT_STATE;

  #define check_serv_state(str, st) 
   (strncmp(str, #st, sizeof(#st) - 1) == 0)

  for(;;) {
    memset(buffer, 0, sizeof(buffer));
    rc = recv(socket_fd, buffer, sizeof(buffer), 0);
    if(rc<0) {
      perror("Error reading back from server");
      exit(1);
    }
    else if(rc == 0) {
      printf("The server has been disconnected. Quitting.n");
      exit(0);
    }

    if(serv_state == INIT_STATE) {
      char *part = strtok(buffer,"n");
      do {
    if(check_serv_state(part, READY)) {
      printf("Server is readyn");
      serv_state = READY_STATE;
    }
    else 
      printf("Server: [[[%s]]]n", part);
      } while((part = strtok(NULL, "n")));

      if(serv_state != READY_STATE)
     continue;
      } else
     printf("#server: %s", buffer);

      memset(buffer, 0, sizeof(buffer));
      printf("#client: ");
      fgets(buffer, sizeof(buffer)/sizeof(buffer[0]) - 1, stdin);

      if(send(socket_fd, buffer, strlen(buffer), 0) < 0)
    {
      perror("Error sending message");
      exit(1);
    }
    }

  close(socket_fd);
  return 0;
}
  

Редактировать:

Как сказал @Olaf Dietsche, это была fgets ошибка. Теперь мне интересно, почему сервер не обрабатывает cd .. команду.

Ответ №1:

Если ваш сервер не отправляет какое-либо приветственное сообщение, клиент «зависает», потому что он ожидает ввода от сервера

 for (;;) {
    memset(buffer, 0, sizeof(buffer));
    rc = recv(socket_fd, buffer, sizeof(buffer), 0);
  

В то же время сервер ожидает ввода от клиента. Итак, оба ждут друг друга. Вот причина, по которой нет прогресса.


Чтобы исправить это, вы должны сначала прочитать ввод от пользователя и отправить этот ввод от клиента на сервер. Например. переместите это из конца в начало цикла

 printf("#client: ");
fgets(buffer, sizeof(buffer)/sizeof(buffer[0]) - 1, stdin);

if (send(socket_fd, buffer, strlen(buffer), 0) < 0) {
    perror("Error sending message");
    exit(1);
}
  

Другая ошибка может быть fgets

Синтаксический анализ останавливается, если происходит конец файла или найден символ новой строки, и в этом случае str будет содержать этот символ новой строки.

Поскольку вы отправляете весь буфер, включая последнюю новую строку, сервер также должен проверить наличие этой новой строки

 if (strcmp(input, "lsn") == 0) {
  

В качестве альтернативы, вы можете удалить его перед отправкой запроса на сервер

 fgets(buffer, sizeof(buffer) / sizeof(buffer[0]) - 1, stdin);
int len = strlen(buffer);
if (buffer[len - 1] == 'n') {
    buffer[len - 1] = 0;
    --len;
}

if (send(socket_fd, buffer, len, 0) < 0) {
  

cd Команда является особенной в том смысле, что она изменяет каталог только в вызывающем процессе. При использовании popen команда будет обработана дочерним процессом

Функция popen () открывает процесс путем создания канала, разветвления и вызова оболочки.

Аргумент команды является указателем на строку с нулевым завершением, содержащую командную строку оболочки. Эта команда передается в /bin/sh с использованием флага -c; интерпретация, если таковая имеется, выполняется оболочкой.

Это означает, что сервер вызывает popen , который создает другой процесс, и этот другой процесс выполняет chdir .

Если вы хотите, чтобы серверный процесс менял каталоги, вы должны не использовать popen , а делать chdir на самом сервере.

Комментарии:

1. Дело в том, что, если я опущу `if (strcmp (input, «ls») == 0)`, код будет работать при ls печати команды с while((c = getc(fp)) != EOF) циклом. И да, сервер отправляет приветственное сообщение. Я попробовал ваше решение, оно не помогло.

2. fgets была проблема. Я добавил n символ в обе ls cd .. команды и . Прямо сейчас у меня ситуация, когда сервер обрабатывает cd .. команду, но она не переходит в каталог. Поэтому, если я сделаю ls -> cd .. -> ls я получаю во второй раз тот же список файлов, что и при первом использовании. Почему это так?

3. Ну, вы проверяете cd .. , но не выполняете cd .. . chdir Подробнее см.

4. Не cd .. обрабатывается таким же образом, как ls ? Я имею в виду, я думал popen() , что позаботится об этом.

5. Да, это довольно хорошо отвечает на этот вопрос.