views:

71

answers:

3

Hi, I'm doing client/server interaction with sockets in C. What I'm trying to do is have the client request a file to be read on the server, the server return a buffer of the file contents, and the client print out the file.

Though I was able to accomplish the server sending a buffer of a file to the client & the client printing it out successfully, I can't seem to get the server to successfully read a filename sent by the client.

Here is what I mean:

///This is the structure of the message that gets sent back and forth
struct message { 
int code; //Just indicates 1 or 2 for readfile or writefile
int size;
char buffer[256]; //This will hold the filename when client sends readfile request
 };

Works:

char *filename = "test.c";
infile = open(filename, O_RDONLY);
//Send the file and everything back to the client

Doesn't Work:

while( read(sockfd, &msg, sizeof(int) * 2) > 0) {
        if(msg.code == 1) { //Perform a read operation
            int infile, filesize, rr;
            int length;
            char output[256];
            size_t rb = 0;

            while(rb < msg.size) { 
                ssize_t r = read(sockfd, msg.buffer, msg.size - rb);
                if(r < 0) { 
                    error(sockfd, r, msg.buffer);
                    break;
                }
                rb += r;
            }

            msg.buffer[rb] = '\0';  
         //This has been printing out the correct amount
            printf("\nBytes read: %i", rb); 
         //This has also been printing out properly
            printf("\nmsg.buffer: %s", msg.buffer); 

            infile = open(msg.buffer, O_RDONLY);

I've edited to show the current state my program is in, and is still not working. Before, I had a strcpy that was improperly placed.

+1  A: 

You are not checking return value of read(2), which tells how many bytes have been read, - your message might not be read in full, so the file name can be truncated.

Edit:

@Amardeep here has better eyes them me - you are corrupting your memory with strcpy(3) into space pointed to by filename pointer variable, which looks like uninitialized.

Nikolai N Fetissov
Thanks, but I return the buffer that was sent to the server back to the client, and it (seems) to be the same:Usage: <readfile/writefile> <filename/filecontents> Input: readfile Makefile Sending: Code: 1 Size: 9 Buffer: Makefile Received: Code: -1 Size: 9 ***Begin stream*** errorReceived buffer... Makefile ***End of stream***
joslinm
I've just done another check. All the bytes were read
joslinm
So why don't you just `printf` it on the server side, or break into debugger and see what's there? BTW, do you check the second `read`?
Nikolai N Fetissov
yes, I check the second read. for some reason I can't printf to the terminal on the server side -- do you know why? I start the server on one terminal, and the client on the other. when I do a printf on the server side, it doesn't do anything despite me fflush(stdout). I checked the length by putting the read in a while loop that loops until bytes read equals the msg.size: while(length < msg.size) length = read(sockfd, msg.buffer, msg.size);it would get stuck otherwise, no?
joslinm
It should be more like `size_t rb = 0; while ( rb < msg.size ) { ssize r = read( fd, buf, msg.size - rb ); if ( r < 0 ) err(); rb += r; }`
Nikolai N Fetissov
There also should be a check for `read` returning `0`, i.e. `EOF`, i.e. other side closing connection.
Nikolai N Fetissov
Start the server under `gdb` and set a break-point before that `printf` and see what's in the `msg.buffer` (`p msg` in `gdb`)
Nikolai N Fetissov
I changed the while loop into that, and was able to get output: Starting program: /home/mark/homework/sockets/tcpserver 7999 Bytes read: 9msg.buffer: MakefileOpened successfully? -1 **note I did pass it Makefile**
joslinm
Where does that `-1` come from?
Nikolai N Fetissov
Sorry, that's what open returns if it fails. I was doing a `printf("Opened successfully? %i", infile);`
joslinm
You should check the value of errno after your open fails, this will tell you exactly why it failed.
Brandon Horsley
+2  A: 

In the code you posted, filename is not initialized. Your compiler should have warned you about this. If it didn't, you're not invoking it correctly; with gcc, do at least gcc -O -Wall.

You also need to allocate memory for the file name if you use strcpy. This step is not necessary in a simple program; making a copy becomes useful if you need to remember the file name after you go on reading from the client. The strdup function combines memory allocation with string copying, it's appropriate here.

You need to check the return value of all system calls. The return value of read tells you how many bytes were read. The call to read(fd,buf,n) is allowed to return less than n bytes if the OS feels like it. If you received fewer bytes than you expected, call read in a loop. Yes, pretty much all programs that call read call it in a loop, it's a basic Unix/POSIX idiom. The fread function does it for you, if you can fit it in.

The code to check the validity of msg.code and msg.size is missing. Since you've allocated 256 bytes in msg.buffer, you must limit msg.size to 255.

Yes, the assignment msg.buffer[msg.size] = '\0' is necessary, because open needs the '\0' character at the end of the name (that's how it knows where the name ends).


I thought maybe strcpy was appropriate

Whenever you're around pointers (which is most of the time in C), you need to think carefully about what you're doing and where those pointers are pointing, and whether there's enough room there for what you want to put. C is an unforgiving language; throwing pointers around is as dangerous as throwing arrows around. Draw diagrams! There are two kinds of C programmers: the ones who draw diagrams on a whiteboard, on paper, in the sand or other medium; and the ones who draw diagrams in their head (the third kind are still trying to figure out why their program to print 1+1 is printing 3).

Gilles
Wow! Thanks a lot! :D Let me see if I can work it out!
joslinm
A: 

Well I've about found out the answer, or at least something close to it. First off, a line break character was getting sent. On the client side, I used fgets(msg.buffer, 256, stdin); to get the message. When I checked the value using printf("File |%s|", msg.buffer), I saw that the second bar was on the next line. I took that line break and overwrote it with a null character at the end.

I also changed to fopen and freads/writes. I added ./ to the beginning of the file statement but I doubt that's needed.. it was just one of my earlier attempts. It now looks something like this:

            size_t rb = 0;
            FILE* file;
            char filename[256];
            while(rb < msg.size) { 
                ssize_t r = read(sockfd, filename, msg.size - rb);
                if(r < 0) { 
                    error(sockfd, r, msg.buffer);
                    goto end;
                }
                rb += r;
            }
            if(strlen(filename) > 253) { 
                error(sockfd, rb, msg.buffer);
                goto end;
            }
            strcpy(msg.buffer, "./");
            strcat(msg.buffer, filename);
            msg.buffer[rb + 1] = '\0';

            file = fopen(msg.buffer, "r");
            if(file == NULL) {
                error(sockfd, rb, msg.buffer);
                printf("Error opening file %s: %s\n", msg.buffer,strerror(errno));
                fflush(stdout);
                goto end;
            }

Thanks to all who helped. I learned a lot (gdb was especially useful -- I never used it before)

joslinm
You have a classic **stack corruption/buffer overrun** problem in the `read(sockfd, filename, msg.size - rb)` part - what happens when client sends `msg.size` of say 432789? Bound that value. ALWAYS SANITY-CHECK ALL INPUT, especially from the network!
Nikolai N Fetissov
Haha thanks, I will! However, on the client, I am doing a `*fgets(char *restrict s, int n, FILE *restrict stream);` and n is set to a maximum of 256 so even if the user -- I think -- types more, it will only read the first 256. This is OK right? At any rate, I appreciate the forewarning. I am very new.
joslinm