Сервер не может правильно прочитать / открыть имя файла, отправленное клиентом в C - PullRequest
2 голосов
/ 28 июля 2010

Я делаю взаимодействие клиент / сервер с сокетами в C. Я пытаюсь сделать так, чтобы клиент запросил файл для чтения на сервере, сервер возвратил буфер содержимого файла и клиентраспечатайте файл.

Хотя мне удалось выполнить отправку сервером буфера файла клиенту, а клиент распечатал его успешно, похоже, сервер не может успешно прочитать имя файлаотправлено клиентом.

Вот что я имею в виду:

///This is the structure of the message that gets sent back and forth
struct message { 
int code; //Just indicates 1 or 2 for readfile or writefile
int size;
char buffer[256]; //This will hold the filename when client sends readfile request
 };

Работы:

char *filename = "test.c";
infile = open(filename, O_RDONLY);
//Send the file and everything back to the client

Не работает:

while( read(sockfd, &msg, sizeof(int) * 2) > 0) {
        if(msg.code == 1) { //Perform a read operation
            int infile, filesize, rr;
            int length;
            char output[256];
            size_t rb = 0;

            while(rb < msg.size) { 
                ssize_t r = read(sockfd, msg.buffer, msg.size - rb);
                if(r < 0) { 
                    error(sockfd, r, msg.buffer);
                    break;
                }
                rb += r;
            }

            msg.buffer[rb] = '\0';  
         //This has been printing out the correct amount
            printf("\nBytes read: %i", rb); 
         //This has also been printing out properly
            printf("\nmsg.buffer: %s", msg.buffer); 

            infile = open(msg.buffer, O_RDONLY);

Я отредактировал, чтобы показать текущее состояние, в котором находится моя программа, и все еще не работает.Раньше у меня была неправильная установка strcpy.

Ответы [ 3 ]

2 голосов
/ 28 июля 2010

В размещенном вами коде filename не инициализируется. Ваш компилятор должен был предупредить вас об этом. Если это не так, вы не вызываете его правильно; с gcc, наберите не менее gcc -O -Wall.

Вам также нужно выделить память для имени файла, если вы используете strcpy. Этот шаг не обязателен в простой программе; создание копии становится полезным, если вам нужно запомнить имя файла после того, как вы продолжите чтение с клиента. Функция strdup сочетает в себе выделение памяти с копированием строк, здесь уместно.

Вам необходимо проверить возвращаемое значение всех системных вызовов. Возвращаемое значение read говорит вам, сколько байтов было прочитано. Вызов read(fd,buf,n) может возвращать меньше n байтов, если ОС чувствует себя так. Если вы получили меньше байтов, чем ожидали, вызовите read в цикле. Да, почти все программы, которые вызывают read, вызывают его в цикле, это базовая идиома Unix / POSIX. Функция fread сделает это за вас, если вы можете в нее вписаться.

Код для проверки правильности msg.code и msg.size отсутствует. Поскольку вы выделили 256 байтов в msg.buffer, вы должны ограничить msg.size 255.

Да, присваивание msg.buffer[msg.size] = '\0' необходимо, потому что open требуется символ '\0' в конце имени (именно так он знает, где заканчивается имя).


Я подумал, что, возможно, strcpy подходит

Всякий раз, когда вы находитесь рядом с указателями (что чаще всего происходит в C), вам нужно тщательно продумать, что вы делаете и куда указывают эти указатели, и достаточно ли там места для того, что вы хотите поместить , С - неумолимый язык; метать стрелки так же опасно, как стрелять. Рисование диаграмм! Существует два вида программистов на С: те, которые рисуют диаграммы на доске, на бумаге, на песке или других носителях; и те, кто рисует диаграммы в своей голове (третий вид все еще пытается выяснить, почему их программа для печати 1+1 печатает 3).

1 голос
/ 28 июля 2010

Вы не проверяете возвращаемое значение read(2), которое сообщает, сколько байтов было прочитано, - ваше сообщение может быть прочитано не полностью, поэтому имя файла можетбыть усеченным.

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

@ Амардип здесь лучше смотрит на меня - вы портите свою память с помощью strcpy(3) в пространство, на которое указывает переменная-указатель filename, которое выглядит как неинициализированное.

0 голосов
/ 29 июля 2010

Ну, я примерно нашел ответ, или, по крайней мере, что-то близкое к нему. Во-первых, отправляется символ перевода строки. На стороне клиента я использовал fgets(msg.buffer, 256, stdin);, чтобы получить сообщение. Когда я проверил значение с помощью printf("File |%s|", msg.buffer), я увидел, что второй столбец находится на следующей строке. Я взял разрыв строки и переписал его нулевым символом в конце.

Я также изменил на fopen и freads / write. Я добавил ./ в начало оператора файла, но сомневаюсь, что это необходимо ... это была лишь одна из моих предыдущих попыток Теперь это выглядит примерно так:

            size_t rb = 0;
            FILE* file;
            char filename[256];
            while(rb < msg.size) { 
                ssize_t r = read(sockfd, filename, msg.size - rb);
                if(r < 0) { 
                    error(sockfd, r, msg.buffer);
                    goto end;
                }
                rb += r;
            }
            if(strlen(filename) > 253) { 
                error(sockfd, rb, msg.buffer);
                goto end;
            }
            strcpy(msg.buffer, "./");
            strcat(msg.buffer, filename);
            msg.buffer[rb + 1] = '\0';

            file = fopen(msg.buffer, "r");
            if(file == NULL) {
                error(sockfd, rb, msg.buffer);
                printf("Error opening file %s: %s\n", msg.buffer,strerror(errno));
                fflush(stdout);
                goto end;
            }

Спасибо всем, кто помог. Я многому научился (GDB был особенно полезен - я никогда не использовал его раньше)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...