javardee
javardee

Reputation: 55

Why is there a difference in bytes when I send a picture file through a socket?

I'm currently building a C/C++ application that sends and receives picture files over a network socket. However, when I send the file over the socket, the destination file has a slight byte-difference when compared to the source file.

In order for this application to function correctly, the destination (client) file must be identical, byte-wise, to the source (server) file.

Client code:

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);
}

Server code:

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);

}

When I receive a file on the client-side, it appears to be the same size, but I'm unable to perform certain compression operations on it, unlike the source file.

When running cmp in my terminal to compare the source and destination files, I get the result:

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

How can I get these files to be byte-wise identical?

Thanks!

Upvotes: 1

Views: 355

Answers (3)

dbush
dbush

Reputation: 223689

There are two main errors.

First, the server writes the size of the file before the content, but the client starts reading the content right away, so the file size becomes part of the content on the client side. The client doesn't need the size, so six this by simply not sending the file size on the server side.

The second issue is in your use of 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);
}

When fread reads the last portion of the file, i.e. an amount smaller than the buffer size, the EOF flag is set. That means feof returns true and you exit the loop before writing the last segment of the file to the socket.

A better way of handling this is loop while 1 or more bytes was read from the file:

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);
}

In addition to this, you don't perform error checking on any of the functions you call. You should check the return value of all socket and file related functions and use perror to print an error message if they fail.

Upvotes: 1

user4581301
user4581301

Reputation: 33932

write(client_socket, &size, sizeof(size));

wrote the size of the image file into the socket (but did not check for a successful write. Always check the return code to make sure you wrote what you thought you did.). The client makes no use of this information and lumps it into the file. Based on how the client is currently coded, the sockets are being closed immediately after finishing the file, you can discard the code computing and sending the length. When the socket is closed, the file is done. There is no need to send the length as a helper.

In addition, the image is almost certainly binary information. Opening the file with

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

and

picture = fopen(fpath, "r");

will open in text mode and possibly perform some conversions (most famous of these is \r\n to \n) and muck up the file. Open with "wb" and "rb" respectively.

Upvotes: 1

Steffen Ullrich
Steffen Ullrich

Reputation: 123260

In the client you send the size of the file and then the content. In the server you assume that only the content is sent, i.e. you store the initially send size information as content in the new file.

Apart from that you just assume that write will actually write all data given, which is not guaranteed. Thus you need to check the actual number of bytes written. And if you are on Windows you better open the files as binary (i.e. "rb" instead of "r", "wb" instead of "w") since you are dealing with binary data here.

Upvotes: 1

Related Questions