Melissia_M
Melissia_M

Reputation: 323

Linux server, sending a file to windows client (Socket)

When i send the file to the client it gets corrupted, and with a size in bytes higher.

I have a version of this server running on Windows and works perfectly,but I'm not having the same result on Linux.

The file size on disk may be the error in time to send the size in bytes to the client that runs on another platform?

the client side works perfectly, as I said I have a version of this server running on Windows, the only difference is in fread: Size = fread(mfcc, 1, min(sizeof(mfcc), FileSize), fp);

fread function is being used correctly?

an expert can analyze and help find the error?

int Socket_Setup::FILE_UPLOAD(int iD, std::string DIR_UPLOAD)
{   
    char Block[1024]; 
    long FileSize;      
    fp = fopen(DIR_UPLOAD.c_str(), "rb");
    if (!fp)
    {
          errno_message.append((char*)strerror(errno));
          FUNCTION_LOG(errno_message);
          return 1;
    }

    fseek(fp, 0, SEEK_END);
    FileSize = ftell(fp);
    rewind(fp);

    long Size_Send = htonl(FileSize);
    Total = FileSize;

    // Sending the file size to the Windows Client  
    iResult = send(client[iD].socket, (const char*)&Size_Send, sizeof(long), 0);        
    if (iResult <= 0)
    {
          errno_message.append((char*)strerror(errno));
          FUNCTION_LOG(errno_message);
          return 1;
    }   

     while (FileSize > 0)
    {
        BytesRead = fread(Block, 1, sizeof(Block), fp);
        if (BytesRead <= 0)
        {
            errno_message.append((char*)strerror(errno));
            FUNCTION_LOG(errno_message);
            fclose(fp);
            return 1;
        }
        if (send(client[iD].socket, Block, BytesRead, 0) != BytesRead)
        {
            errno_message.append((char*)strerror(errno));
            FUNCTION_LOG(errno_message);
            fclose(fp);
            return 1;
        }
        FileSize -= BytesRead;      
    }
    fclose(fp);
    return 0;
}

Upvotes: 4

Views: 842

Answers (2)

Assuming the POSIX sockets (IEEE Std 1003.1, 2013 Edition) are used.

Analysis

Let's consider the following pieces of code:

  1. Sending the file size.

    // Sending the file size to the Windows Client  
    iResult = send(client[iD].socket, (const char*)&Size_Send, sizeof(long), 0);
    if (iResult <= 0)
    {
          errno_message.append((char*)strerror(errno));
          FUNCTION_LOG(errno_message);
          return 1;
    }
    
  2. Sending the "block".

    if (send(client[iD].socket, Block, BytesRead, 0) != BytesRead)
    {
        errno_message.append((char*)strerror(errno));
        FUNCTION_LOG(errno_message);
        fclose(fp);
        return 1;
    }
    

The send() function does not guarantee that all the data will be sent "at once" (i.e. with the single function call):

Return value

Upon successful completion, send() shall return the number of bytes sent. Otherwise, -1 shall be returned and errno set to indicate the error.

send - send a message on a socket, The Open Group Base Specifications Issue 7, IEEE Std 1003.1, 2013 Edition.

As a result, the if (send(client[iD].socket, Block, BytesRead, 0) != BytesRead) check does not seem to be right.

Solution

The return value of the send() function must be used to implement the loop to send all the bytes read previously by the fread() function call. Please see the implementation of the SendAllBytes() function. It should be used to correct the both pieces of code (see the "Analysis" section).

The following code should be considered only as an example:

#include <stdio.h>
#include <sys/socket.h>

...

if (SendAllBytes(socket, &file_size, sizeof(file_size)) != 0) {
    // Error.
    return;
}

...

char buffer[1024];
while (feof(file_stream) == 0) {
    const size_t bytes_read = fread(buffer, sizeof(*buffer), sizeof(buffer) / sizeof(*buffer), file_stream);
    // Upon successful completion, fread() shall return the number of elements
    // successfully read which is less than nitems only if a read error or end-of-file is encountered.
    // If size or nitems is 0, fread() shall return 0 and the contents of the array and the state of the stream remain unchanged.
    // Otherwise, if a read error occurs, the error indicator for the stream shall be set, and errno shall be set to indicate the error.
    // (Source: http://pubs.opengroup.org/onlinepubs/9699919799/functions/fread.html).
    if (bytes_read < sizeof(buffer) / sizeof(*buffer) &&
        ferror(file_stream) != 0) {
        // Error.
        return;
    }

    if (SendAllBytes(socket, buffer, bytes_read) != 0) {
        // Error.
        return;
    }
}

int SendAllBytes(int socket, const char *buffer, size_t bytes_to_send)
{
    size_t total_bytes_sent = 0;
    while (bytes_to_send > 0) {
        const ssize_t bytes_sent = send(socket, &buffer[total_bytes_sent], bytes_to_send, 0);
        // Upon successful completion, send() shall return the number of bytes sent.
        // Otherwise, -1 shall be returned and errno set to indicate the error.
        // (Source: http://pubs.opengroup.org/onlinepubs/9699919799/).
        if (bytes_sent == -1) {
            // Error.
            return -1;
        }

        total_bytes_sent += bytes_sent;
        bytes_to_send -= bytes_sent;
    }
    return 0;
}

Portability note

Consider using fixed-width integer types to enhance the portability. In the described case, the file size is not represented by the fixed-width integer type.

Upvotes: 2

Jeremy Friesner
Jeremy Friesner

Reputation: 73061

I think your problem is here:

iResult = send(client[iD].socket, (const char*)&Size_Send, Size_Send, 0);       

You are sending Size_Send bytes with this call (most of which are miscellaneous memory from after the end of the Size_Send variable), rather than what you probably intended, which is to send sizeof(long) bytes. Replace the second instance of Size_Sent in the above line with sizeof(long) and you'll get a better result.

Upvotes: 3

Related Questions