tags:

views:

119

answers:

6

Hello. I'm trying to send a file to a particular socket using C; here's the code:

int send_file(char *filepath,int sock)
{
    FILE *fp=fopen(filepath,"rb");
    void *buff=malloc(2000);
    int i=1;
    while(i)
    {
        int bytes_read=fread(buff,1,2000,fp);
        i=!(feof(fp));
        int bytes_sent=0;
        while(bytes_sent<bytes_read)
        {
            int n=send(sock,buff+bytes_sent,bytes_read-bytes_sent,0);
            if(n==-1)
                return -1; //failure
            bytes_sent+=n;
        }

    }
    fclose(fp);
    free(buff);
    return 0;
}

When I run this program and try to view the text file in Firefox at http://127.0.0.1:8080/ , a part of the file is cut off from the end if the file size is over 2000 bytes. If I send a picture, only 3/4th of the picture loads (cut off from the bottom).

The function always returns 0 to the caller though. Where does the last chunk of bytes it send()s disappear? Do I need to flush some stream before returning?

Thank you

EDIT: This is a snippet from my main() function:

 send_file(filepath, sock);
 close(sock);
 return 0;
 }
+1  A: 

There's a standard API for this! Use sendfile().

The kernel does the copying without context-switching into your thread, which is substantially more efficient.

Will
That is not standard at all. As per http://linux.die.net/man/2/sendfile - "Not specified in POSIX.1-2001, or other standards. Other Unix systems implement sendfile() with different semantics and prototypes. It should not be used in portable programs."
R Samuel Klatchko
+1  A: 

You should never use feof(), it is almost always the wrong thing to do. Instead, use the return value of fread() to determine when you have read everything - so long as it is non-zero, you need to keep reading and sending. In pseudo code:

while(1) {
   r = fread( ... );
   if ( r == 0 ) {
      break;
   }
   send( .. );
}
anon
Modified code, still getting the same problem..the text I see in my browser is cut off at one point. :|
io555782
@io555782 The code you posted will not work so long as you use eof()
anon
I modified it as you said (without feof()) http://pastebin.com/f29a656b0 ; still cuts off some text in the end.
io555782
A: 

I cannot see the rest of your program but I suspect that you are exiting before closing your socket.

If your program exits before the socket is finished sending its data, the OS is not obligated to do anything with the remaining unsent data and will probably throw it out.

Zan Lynx
A: 

The code you posted looks okay to me. The feof issue may exist as Neil says, although it works fine using Microsoft's compiler. I tested it as written with a stub for the send call, and all data was "sent". It would seem to me that the error maybe lies at the receiving end. An easy test would be to simply print the file lengths that you send and read at both ends. Make sure the recv call gets all flushed data.

Mark Wilkins
Thanks. The receiving end I used to test this code is a web browser though, not a program written by me.
io555782
Ah - right. You mention that in your post and I promptly forgot. I think Zan may be onto something.
Mark Wilkins
A: 

If you're looking at the file with a web browser, you should be sending an HTTP header before you send the actual data.

I am just guessing, but most likely, in the header, you're sending the size of the data as 2000 bytes. So, even though you're sending more than 2000 bytes, the browser isn't expecting them, and displays only the first 2000 bytes.

Also, since you're closing the connection, you should send Connection: close header to the clients.

Alok
A: 

Are you reading all of the data that the web browser sent to you? If you're not (which is understandable, if you're not actually writing a web server), then calling close() with unread data is provoking an abort on the TCP connection instead of a clean shutdown.

A clean shutdown waits for all outstanding data to be sent; an abort does not, which can cause that outstanding data to be lost.

For testing purposes, you should be able to fix it by putting this in at the end of the sending function (before you free buff):

shutdown(sock, SHUT_WR);    /* Send EOF to web browser */
while (recv(sock, buff, 2000, 0) > 0)
    ;  /* Read and discard until we see the browser's EOF (or an error) */
caf