c отправить и получить файл

Это серверная часть (файл отправки):

offset = 0;
for (size_to_send = fsize; size_to_send > 0; ){
  rc = sendfile(newsockd, fd, &offset, size_to_send);
  if (rc <= 0){
    perror("sendfile");
    onexit(newsockd, sockd, fd, 3);
  }
  offset += rc;
  size_to_send -= rc;
}
close(fd); /* la chiusura del file va qui altrimenti rischio loop infinito e scrittura all'interno del file */

memset(buffer, 0, sizeof(buffer));
strcpy(buffer, "226 File Successfully transfered\n");
if(send(newsockd, buffer, strlen(buffer), 0) < 0){
  perror("Errore durante l'invio 226");
  onexit(newsockd, sockd, 0, 2);
}
memset(buffer, 0, sizeof(buffer));

и это часть клиентской (recv-файла) части:

    fsize_tmp = fsize;
    sInfo.filebuffer = malloc(fsize);
  if(sInfo.filebuffer == NULL){
    perror("malloc");
    onexit(sockd, 0, fd, 4);
  }

  while(((uint32_t)total_bytes_read != fsize) && ((nread = read(sockd, sInfo.filebuffer, fsize_tmp)) > 0)){
    if(write(fd, sInfo.filebuffer, nread) != nread){
            perror("write RETR");
            onexit(sockd, 0, 0, 1);
        }
        total_bytes_read += nread;
        fsize_tmp -= nread;
    }
  close(fd); /* la chiusura del file va qui altrimenti client entra in loop infinito e si scrive all'interno del file */

    memset(buffer, 0, sizeof(buffer));
    if(recv(sockd, buffer, 34, 0) < 0){
    perror("Errore ricezione 226");
    onexit(sockd, 0, 0, 1);
  }
  printf("%s", buffer);
  memset(buffer, 0, sizeof(buffer));
  memset(dirpath, 0, sizeof(dirpath));
  free(sInfo.filebuffer);

Проблема в том, что строка "226 File etc" записана внутри файла, который был отправлен.
Я попытался выполнить небольшую отладку, поэтому Я добавил printf после цикла for (файл отправки сервера) и printf после цикла while (клиент), и я заметил, что файл отправлен, но на клиенте он не выходит из while, потому что printf isn не напечатал ...
Почему у меня такое странное поведение ??
br>

РЕДАКТИРОВАТЬ:
Сервер отправляет размер файла клиенту с помощью этого кода:

  fd = open(filename, O_RDONLY);
    if(fd < 0){
    error!!
    }

    if(fstat(fd, &fileStat) < 0){
        perror("Errore fstat");
        onexit(newsockd, sockd, fd, 3);
    }
    fsize = fileStat.st_size;
    if(send(newsockd, &fsize, sizeof(fsize), 0) < 0){
      perror("Errore durante l'invio della grandezza del file\n");
      onexit(newsockd, sockd, fd, 3);
     }

клиент получает fsize от сервера с помощью этого кода:

if(read(sockd, &fsize, sizeof(fsize)) < 0){
    perror("Errore durante ricezione grandezza file\n");
    onexit(sockd, 0 ,0 ,1);
}
fd = open(sInfo.filename, O_CREAT | O_WRONLY, 0644);
if (fd  < 0) {
    perror("open");
    onexit(sockd, 0 ,0 ,1);
}
fsize_tmp = fsize;

оба fsize объявлены как _9 _...


person polslinux    schedule 14.08.2012    source источник
comment
Возможный дубликат Отправить и получить файл при программировании сокетов в Linux с C / C ++ (GCC / G ++)   -  person Ciro Santilli 新疆再教育营六四事件ۍ    schedule 15.06.2017


Ответы (5)


Попробуйте этот код:

Сторона клиента:

/* Client code */
/* TODO : Modify to meet your need */
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netinet/in.h>

#define PORT_NUMBER     5000
#define SERVER_ADDRESS  "192.168.1.7"
#define FILENAME        "/home/toc/foo.c"

int main(int argc, char **argv)
{
        int client_socket;
        ssize_t len;
        struct sockaddr_in remote_addr;
        char buffer[BUFSIZ];
        int file_size;
        FILE *received_file;
        int remain_data = 0;

        /* Zeroing remote_addr struct */
        memset(&remote_addr, 0, sizeof(remote_addr));

        /* Construct remote_addr struct */
        remote_addr.sin_family = AF_INET;
        inet_pton(AF_INET, SERVER_ADDRESS, &(remote_addr.sin_addr));
        remote_addr.sin_port = htons(PORT_NUMBER);

        /* Create client socket */
        client_socket = socket(AF_INET, SOCK_STREAM, 0);
        if (client_socket == -1)
        {
                fprintf(stderr, "Error creating socket --> %s\n", strerror(errno));

                exit(EXIT_FAILURE);
        }

        /* Connect to the server */
        if (connect(client_socket, (struct sockaddr *)&remote_addr, sizeof(struct sockaddr)) == -1)
        {
                fprintf(stderr, "Error on connect --> %s\n", strerror(errno));

                exit(EXIT_FAILURE);
        }

        /* Receiving file size */
        recv(client_socket, buffer, BUFSIZ, 0);
        file_size = atoi(buffer);
        //fprintf(stdout, "\nFile size : %d\n", file_size);

        received_file = fopen(FILENAME, "w");
        if (received_file == NULL)
        {
                fprintf(stderr, "Failed to open file foo --> %s\n", strerror(errno));

                exit(EXIT_FAILURE);
        }

        remain_data = file_size;

        while ((remain_data > 0) && ((len = recv(client_socket, buffer, BUFSIZ, 0)) > 0))
        {
                fwrite(buffer, sizeof(char), len, received_file);
                remain_data -= len;
                fprintf(stdout, "Receive %d bytes and we hope :- %d bytes\n", len, remain_data);
        }
        fclose(received_file);

        close(client_socket);

        return 0;
}

Сторона сервера:

/* Server code */
/* TODO : Modify to meet your need */
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/sendfile.h>

#define PORT_NUMBER     5000
#define SERVER_ADDRESS  "192.168.1.7"
#define FILE_TO_SEND    "hello.c"

int main(int argc, char **argv)
{
        int server_socket;
        int peer_socket;
        socklen_t       sock_len;
        ssize_t len;
        struct sockaddr_in      server_addr;
        struct sockaddr_in      peer_addr;
        int fd;
        int sent_bytes = 0;
        char file_size[256];
        struct stat file_stat;
        int offset;
        int remain_data;

        /* Create server socket */
        server_socket = socket(AF_INET, SOCK_STREAM, 0);
        if (server_socket == -1)
        {
                fprintf(stderr, "Error creating socket --> %s", strerror(errno));

                exit(EXIT_FAILURE);
        }

        /* Zeroing server_addr struct */
        memset(&server_addr, 0, sizeof(server_addr));
        /* Construct server_addr struct */
        server_addr.sin_family = AF_INET;
        inet_pton(AF_INET, SERVER_ADDRESS, &(server_addr.sin_addr));
        server_addr.sin_port = htons(PORT_NUMBER);

        /* Bind */
        if ((bind(server_socket, (struct sockaddr *)&server_addr, sizeof(struct sockaddr))) == -1)
        {
                fprintf(stderr, "Error on bind --> %s", strerror(errno));

                exit(EXIT_FAILURE);
        }

        /* Listening to incoming connections */
        if ((listen(server_socket, 5)) == -1)
        {
                fprintf(stderr, "Error on listen --> %s", strerror(errno));

                exit(EXIT_FAILURE);
        }

        fd = open(FILE_TO_SEND, O_RDONLY);
        if (fd == -1)
        {
                fprintf(stderr, "Error opening file --> %s", strerror(errno));

                exit(EXIT_FAILURE);
        }

        /* Get file stats */
        if (fstat(fd, &file_stat) < 0)
        {
                fprintf(stderr, "Error fstat --> %s", strerror(errno));

                exit(EXIT_FAILURE);
        }

        fprintf(stdout, "File Size: \n%d bytes\n", file_stat.st_size);

        sock_len = sizeof(struct sockaddr_in);
        /* Accepting incoming peers */
        peer_socket = accept(server_socket, (struct sockaddr *)&peer_addr, &sock_len);
        if (peer_socket == -1)
        {
                fprintf(stderr, "Error on accept --> %s", strerror(errno));

                exit(EXIT_FAILURE);
        }
        fprintf(stdout, "Accept peer --> %s\n", inet_ntoa(peer_addr.sin_addr));

        sprintf(file_size, "%d", file_stat.st_size);

        /* Sending file size */
        len = send(peer_socket, file_size, sizeof(file_size), 0);
        if (len < 0)
        {
              fprintf(stderr, "Error on sending greetings --> %s", strerror(errno));

              exit(EXIT_FAILURE);
        }

        fprintf(stdout, "Server sent %d bytes for the size\n", len);

        offset = 0;
        remain_data = file_stat.st_size;
        /* Sending file data */
        while (((sent_bytes = sendfile(peer_socket, fd, &offset, BUFSIZ)) > 0) && (remain_data > 0))
        {
                fprintf(stdout, "1. Server sent %d bytes from file's data, offset is now : %d and remaining data = %d\n", sent_bytes, offset, remain_data);
                remain_data -= sent_bytes;
                fprintf(stdout, "2. Server sent %d bytes from file's data, offset is now : %d and remaining data = %d\n", sent_bytes, offset, remain_data);
        }

        close(peer_socket);
        close(server_socket);

        return 0;
}

РЕДАКТИРОВАТЬ: добавление объяснения от человека о offset

На странице руководства файла отправки говорится:

Если смещение не равно NULL, то оно указывает на переменную, содержащую смещение файла, с которого sendfile () начнет чтение данных из in_fd.
Когда sendfile () вернется, эта переменная будет установлена ​​на смещение байт, следующий за последним прочитанным байтом.

person TOC    schedule 15.08.2012
comment
я решил свою проблему: D большое спасибо! Моя ошибка заключалась в том, что я использовал & fsize вместо массива символов, чтобы получить размер файла :) - person polslinux; 16.08.2012
comment
@polslinux: Добро пожаловать: -. хорошо, отлично. Также была проблема с увеличением смещения на стороне сервера. - person TOC; 16.08.2012
comment
@polslinux: Да, в вашем коде вы увеличиваете смещение (offset + = rc;), в этом нет необходимости (в моем коде на стороне сервера я даю вам два fprintf, чтобы увидеть, что происходит со смещением при отправке данных) - person TOC; 16.08.2012
comment
Я тестирую код для отправки простого текстового файла с приветствием в нем. Однако полученный мной файл клиентского размера всегда пуст. После ссылки на этот пост tldp.org/LDP/LGNET/91 /misc/tranter/server.c.txt и прочитав предупреждение, я обнаружил предупреждение, в котором говорится, что переменная смещения не должна быть int, поэтому я меняю тип смещения с int на off_t, и файл sendfile работает нормально. - person juejiang; 12.10.2015

Клиент не знает, когда файл заканчивается. Он просто читает, пока не получит fsize байт. В вашей текущей реализации клиент будет работать только с файлами размером ровно fsize байта.

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

person Klas Lindbäck    schedule 14.08.2012
comment
я понятия не имею, что делать, чтобы использовать http xD - person polslinux; 14.08.2012
comment
Я прочитал ваше обновление. Кажется, не хватает только самой команды send для размера файла. - person Klas Lindbäck; 15.08.2012
comment
ты прав! я обновил свой вопрос! Я также обнаружил проблему: сервер отправляет правильный fsize, но клиент получает очень большой fsize :( :( например, если fsize на сервере равен 1048, клиент получает 20209320 O.o - person polslinux; 15.08.2012
comment
Сервер - Linux, а клиент - Windows? Используйте htonl на отправляющей стороне, чтобы преобразовать размер в сетевой порядок байтов. Используйте ntohl на принимающей стороне, чтобы преобразовать его из сети в порядок байтов хоста. - person Klas Lindbäck; 15.08.2012
comment
и клиент, и сервер находятся на Ubuntu 12.04! - person polslinux; 15.08.2012

Я думаю, вам следует предоставить более подробный код, по крайней мере, с объявлениями и инициализацией используемых переменных.

Как клиентская часть получает значение fsize?

Вы также должны проверить условие while следующим образом:

while(((uint32_t)total_bytes_read < fsize) && ( .....

Не используйте «! =», потому что, если (по неизвестной причине) total_bytes_read станет больше, чем fsize, вы застрянете в бесконечном цикле, пока соединение сокета не будет закрыто (и чтение вернет ошибка).

Я также думаю (но не уверен), что вам следует использовать recv вместо read в своей клиентской части.

person phsym    schedule 14.08.2012
comment
В этом случае нет разницы между recv(2) и read(2). - person Nikolai Fetissov; 14.08.2012
comment
fstat(fd, &fileStat); fsize = fileStat.st_size; чтобы получить более простой пример, я не проверяю ошибки - person polslinux; 14.08.2012

Вы всегда должны определять размер файла, который вы отправляете как часть протокола, например, вы можете отправить размер файла как первые 4 (или более, в зависимости от размеров файла, которые вы ожидаете обрабатывать) до фактического ручей.

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

person Ishay Peled    schedule 14.08.2012

Вы неправильно используете sendfile API. Поскольку вы передаете в третьем параметре значение, отличное от NULL, sendfile обновит смещение за вас. Но поскольку ваш цикл отправки также обновляет смещение, вы пропустите некоторые байты в случае, если sendfile не смог отправить весь файл за один вызов.

На странице руководства:

Если offset не NULL, то он указывает на переменную, содержащую смещение файла, с которого sendfile() начнет чтение данных из in_fd. Когда sendfile() возвращается, эта переменная будет установлена ​​на смещение байта, следующего за последним байтом, который был прочитан.

Вы должны удалить эту строку из цикла отправки:

  offset += rc;

Изменить: В своем обновлении вы используете fpl для получения fstat информации из вашего файла, но в своем sendfile коде вы используете fd. Я хотел бы убедиться, что это именно то, что вы ожидаете от них. (Это было исправлено в еще одном обновлении.) В любом случае я написал протестируйте программу, используя предоставленный вами код (с предложенным мной исправлением), и, похоже, она отлично работает с файлом размером менее 2 КБ и файлом 3 МБ.

person jxh    schedule 14.08.2012
comment
не работает ... строка 226 и т. д. и т. д. печатается внутри файла :( - person polslinux; 14.08.2012
comment
также я думаю, что использование var является правильным, потому что man sendfile report: Если смещение не равно NULL, тогда он указывает на переменную, содержащую смещение файла, с которой sendfile () начнет читать данные из in_fd. - person polslinux; 14.08.2012
comment
@polslinux: я обновил ответ, добавив полную цитату из справочной страницы. - person jxh; 14.08.2012
comment
@polslinux: Кроме того, как и другие просили вас сделать, пожалуйста, укажите, как вы убедитесь, что значение fsize одинаково для отправителя и получателя. - person jxh; 14.08.2012
comment
Извините, я скопировал не ту часть xD Я обновил свой вопрос! - person polslinux; 14.08.2012
comment
однако это 2 разные программы, 1 для отправки и 1 для приема :) - person polslinux; 14.08.2012
comment
@polslinux: Вы можете использовать мою программу для создания двух разных программ. Строки кода, которые выполняются, одинаковы, независимо от того, разбили ли вы его на отдельные программы. - person jxh; 14.08.2012
comment
Я обнаружил проблему: клиент получает неправильный размер файла. Например, если размер файла 1048, сервер отправляет 1048, а клиент получает 20203902. - person polslinux; 15.08.2012
comment
@polslinux: если вы работаете в гетерогенной среде, это важный факт, который следует опустить в описании вашей проблемы. Но хорошая работа по отслеживанию. - person jxh; 15.08.2012
comment
да извини! Я работаю на Ubuntu 12.04 с обоими из них (клиент и сервер) - person polslinux; 15.08.2012