Почему разница в байтах при отправке файла изображения через сокет? - PullRequest
1 голос
/ 17 марта 2020

В настоящее время я создаю приложение C / C ++, которое отправляет и получает файлы изображений через сетевой сокет. Однако когда я отправляю файл через сокет, целевой файл имеет небольшую разницу в байтах по сравнению с исходным файлом.

Чтобы приложение работало правильно, целевой файл (клиент) должен быть Идентично, побайтно, к исходному (серверному) файлу.

Код клиента:

int client_app(int argc, char *argv[]) {
    // create a socket
    int network_socket;
    network_socket = socket(AF_INET, SOCK_STREAM, 0);

    // specify an address for the socket
    struct sockaddr_in server_address;
    server_address.sin_family = AF_INET;
    server_address.sin_port = htons(9002);
    server_address.sin_addr.s_addr = INADDR_ANY;

    // connect returns an integer
    int connection_status = connect(network_socket, (struct sockaddr *) &server_address, sizeof(server_address));

    if (connection_status == -1) {
        printf("Error making connection to remote socket \n\n");
    }

    // read picture byte array
    printf("Reading picture byte array...\n");
    char p_array[BUFSIZ];

    // convert it back into a pic
    printf("Converting byte array to DNG...\n");
    FILE *image = fopen("out_compressed.GPR", "w");
    int nb;
    while ((nb = read(network_socket, p_array, BUFSIZ)) > 0) {
        fwrite(p_array, 1, nb, image);
        bzero(p_array, BUFSIZ);
    }
    fclose(image);

    // and then close the socket
    close(network_socket);
}

Код сервера:

int server_app(int argc, char *argv[]) {
    char *process_argv[100];
    *process_argv = *argv;
    int process_argc = 0;
    for (int i = 1; i < 6; i++) {
        process_argv[i] = argv[i + 1];
        process_argc++;
    }
    process(process_argc, process_argv);

    // create the server socket
    int server_socket;
    server_socket = socket(AF_INET, SOCK_STREAM, 0);

    // define the port number
    int port = 9002;

    // define the server address
    struct sockaddr_in server_address;
    server_address.sin_family = AF_INET;
    server_address.sin_port = htons(port);
    server_address.sin_addr.s_addr = INADDR_ANY;

    // bind the socket to our specified IP and port
    bind(server_socket, (struct sockaddr *) &server_address, sizeof(server_address));

    // listen for connections on the socket
    listen(server_socket, 5);

    // create client socket for the server to send data to
    int client_socket;
    client_socket = accept(server_socket, nullptr, nullptr);

    // get picture size
    char *fpath = process_argv[4];
    FILE *picture;
    picture = fopen(fpath, "r");
    int size;
    fseek(picture, 0, SEEK_END);
    size = ftell(picture);
    fseek(picture, 0, SEEK_SET);

    // send picture size
    write(client_socket, &size, sizeof(size));

    // send picture as byte array
    char send_buffer[BUFSIZ];
    int nb = fread(send_buffer, 1, sizeof(send_buffer), picture);

    while (!feof(picture)) {
        write(client_socket, send_buffer, nb);
        nb = fread(send_buffer, 1, sizeof(send_buffer), picture);
    }

    // then close the sockets
    close(server_socket);
    close(client_socket);

}

Когда я получаю файл на на стороне клиента он кажется того же размера, но я не могу выполнить некоторые операции сжатия на нем, в отличие от исходного файла.

При запуске cmp в моем терминале для сравнения источника и целевые файлы, я получаю результат:

compressed.GPR out_compressed.GPR differ: char 1, line 1

Как я могу сделать так, чтобы эти файлы были побайтно идентичными?

Спасибо!

Ответы [ 3 ]

1 голос
/ 17 марта 2020
write(client_socket, &size, sizeof(size));

записал размер файла изображения в сокет (но не проверил успешность записи. Всегда проверяйте код возврата, чтобы убедиться, что вы написали то, что, как вы думали, вы сделали.). Клиент не использует эту информацию и помещает ее в файл. Исходя из того, как в данный момент кодируется клиент, сокеты закрываются сразу после завершения файла, вы можете отказаться от вычисления кода и отправки длины. Когда сокет закрыт, файл готов. Нет необходимости отправлять длину в качестве помощника.

Кроме того, изображение почти наверняка представляет собой двоичную информацию. Открытие файла с

FILE *image = fopen("out_compressed.GPR", "w");

и

picture = fopen(fpath, "r");

откроет в текстовом режиме и, возможно, выполнит некоторые преобразования (наиболее известные из них \r\n в \n) и испортит файл. Открыть с помощью "wb" и "rb" соответственно.

1 голос
/ 17 марта 2020

Есть две основные ошибки.

Во-первых, сервер записывает размер файла перед содержимым, но клиент сразу начинает читать содержимое, поэтому размер файла становится частью содержимого на сторона клиента. Клиенту не нужен размер, поэтому в шесть раз просто не отправляйте размер файла на стороне сервера.

Вторая проблема заключается в использовании feof:

int nb = fread(send_buffer, 1, sizeof(send_buffer), picture);

while (!feof(picture)) {
    write(client_socket, send_buffer, nb);
    nb = fread(send_buffer, 1, sizeof(send_buffer), picture);
}

Когда fread читает последнюю часть файла, т.е. размер меньше размера буфера, устанавливается флаг EOF. Это означает, что feof возвращает true, и вы выходите из l oop перед записью последнего сегмента файла в сокет.

Лучшим способом обработки является l oop, тогда как 1 или более байтов были читать из файла:

int nb = fread(send_buffer, 1, sizeof(send_buffer), picture);

while (nb > 0) {
    write(client_socket, send_buffer, nb);
    nb = fread(send_buffer, 1, sizeof(send_buffer), picture);
}

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

1 голос
/ 17 марта 2020

В клиенте вы отправляете размер файла, а затем содержимое. На сервере вы предполагаете, что отправляется только контент, т.е. вы сохраняете информацию о размере первоначально отправленной информации как контент в новом файле.

Кроме того, вы просто предполагаете, что запись на самом деле запишет все данные, что не гарантировано. Таким образом, вам необходимо проверить фактическое количество записанных байтов. И если вы используете Windows, вам лучше открыть файлы как двоичные файлы (т. Е. «Rb» вместо «r», «wb» вместо «w»), поскольку здесь вы имеете дело с двоичными данными.

...