thedafferg
thedafferg

Reputation: 99

File transfer in C - Linux

I'm trying to transfer a file from server to client. I first send the name of the file I want to receive to the server, the server opens the file and writes its contents into a buffer and sends it back to the client. The client then copies the contents of that buffer into a newly created file to duplicate the contents of the server file.

When "Receive.txt" is created, only the FIRST word of the file I opened is copied into the file I created. Where am I going wrong?

server.c sending part:

if(checkCommand){               
    char *tmp = buf + 4;
    char data[MAX_BLOCK_SIZE];
    FILE *fp;

    printf("File name: %s\n", tmp);
    fp = fopen(tmp, "r");
    if(fp == NULL){
        printf("File not found\n");
        exit(1);
    }
            
    do{
        fscanf(fp, "%s", buf);
        nw = write(sd, buf, MAX_BLOCK_SIZE);
    } while(fscanf(fp, "%s", buf) != EOF);          

}

client.c receiving part:

else if(getCommand){
    FILE *fp;
            
    write(sd, buf, MAX_BLOCK_SIZE);
    read(sd, buf, MAX_BLOCK_SIZE);
    fp = fopen("receive.txt", "w");
    if(fp == NULL){
        printf("File could not be opened.\n");
        exit(1);
    }
    fprintf(fp, "%s", buf);
}

Upvotes: 0

Views: 566

Answers (1)

Roberto Caboni
Roberto Caboni

Reputation: 7490

As correctly reported in comments section, the core of your issue consists in fscanf() being called both for reading data and for detecting EOF in order to quit the loop. But the latter actually consume data as well!

If I execute your program passing the following input file

Yesterday
All my troubles seemed so far away
Now it looks as though they're here to stay

and printing the output to stdout with printf("%s\n", buf); (instead of pushing data to a socket), that's what I get:

Yesterday
my
seemed
far
Now
looks
though
here
stay

So I get not only the first word, but one every two words.

Furthermore you write to the socket MAX_BLOCK_SIZE bytes whatever is the number of valid bytes you correctly read from file, so buf will contain MAX_BLOCK_SIZE - strlen(buf) bytes of garbage.

This sending loop will fix most of the issues mentioned above:

while(fscanf(fp, "%s", buf) != EOF)
{        
    write(sd, buf, strlen(buf));
}

Please note how you still would have something to care about:

  1. are we sure that all words fit a MAX_BLOCK_SIZE big buffer? A word logger than the block will cause out of bound access (and undefined behavior), so a way to limit the number of acquired characters should be implemented
  2. are we sure that all the data is sent in one shot? Probably yes, as the words are relatively short, but generally speaking the return value of the write() function should be checked, and if it is different from the amount of data to be sent the code should manage a way to send the remainder (or even exit the loop if the return value is negative!)

I'm not going to address these issues in this answer, but it was correct mentioning them. What I would like to emphatize, istead, is that sending data in this way will generate an output without whitespaces_. Something like:

YesterdayAllmytroublesseemedsofarawayNowitlooksasthoughthey'reheretostay

Even assuming that you need just a text file transfer, probably it is not what you want. For this reason I suggest implementing a binary trasfer using fread() in this way

#include<stdio.h>
#include <stdlib.h>

#define MAX_BLOCK_SIZE 1024

char buf[MAX_BLOCK_SIZE];

int main(void)
{
    char tmp[] = "srcFile.txt";
    FILE * fp;
    int rd;
    
    printf("File name: %s\n", tmp);
    
    fp = fopen(tmp, "rb");
    if(fp == NULL)
    {
        printf("File not found\n");
        exit(1);
    }
            
    while(( rd = fread(buf, 1, MAX_BLOCK_SIZE, fp)) > 0)
    {
        write(sd, buf, rd);
        //printf("%s\n", buf);
    }
    // You can call feof() or ferror() in order to discover if either EOF or an error occurred
    
    return 0;
}

Uncommenting the printf what I get is exactly the input file. Please note how it would not necessary, on Linux environment, to select the binary mode with b, as

The mode string can also include the letter 'b' either as a last character or as a character between the characters in any of the two-character strings described above. This is strictly for compatibility with C89 and has no effect; the 'b' is ignored on all POSIX conforming systems, including Linux. (Other systems may treat text files and binary files differently, and adding the 'b' may be a good idea if you do I/O to a binary file and expect that your program may be ported to non-UNIX environments.)

(the emphasis is mine).


Just some notes about the receiver part:

  1. I cannot comment the initial write(sd, buf, MAX_BLOCK_SIZE); because I'm not sure about the detais of your application, but it look strange. Make sure to send consistent data, in any case.
  2. Always check the return values of read and write! Not only they can fail (and the error, that you can get querying errno, must be properly managed) but they can also read/write from/to socket less bytes than requested
  3. In the general case of a binary file, writing data to the output file with fprintf(fp, "%s", buf); is not safe, as buf could contain bytes that are not printable or, even worse, it won't be NULL-terminated. Be aware that binary data could contain the '\0' character inside it, and that would result in an unexpected truncation. Use fwrite(), instead.

Upvotes: 1

Related Questions