Question

This is the server (sendfile) part:

offset = 0;
for (size_to_send = fsize; size_to_send > 0; ){
  rc = sendfile(newsockd, fd, &offset, size_to_send);
  if (rc <= 0){
    perror("sendfile");
    onexit(newsockd, sockd, fd, 3);
  }
  offset += rc;
  size_to_send -= rc;
}
close(fd); /* la chiusura del file va qui altrimenti rischio loop infinito e scrittura all'interno del file */

memset(buffer, 0, sizeof(buffer));
strcpy(buffer, "226 File Successfully transfered\n");
if(send(newsockd, buffer, strlen(buffer), 0) < 0){
  perror("Errore durante l'invio 226");
  onexit(newsockd, sockd, 0, 2);
}
memset(buffer, 0, sizeof(buffer));

and this is the part of the client (recv file) part:

    fsize_tmp = fsize;
    sInfo.filebuffer = malloc(fsize);
  if(sInfo.filebuffer == NULL){
    perror("malloc");
    onexit(sockd, 0, fd, 4);
  }

  while(((uint32_t)total_bytes_read != fsize) && ((nread = read(sockd, sInfo.filebuffer, fsize_tmp)) > 0)){
    if(write(fd, sInfo.filebuffer, nread) != nread){
            perror("write RETR");
            onexit(sockd, 0, 0, 1);
        }
        total_bytes_read += nread;
        fsize_tmp -= nread;
    }
  close(fd); /* la chiusura del file va qui altrimenti client entra in loop infinito e si scrive all'interno del file */

    memset(buffer, 0, sizeof(buffer));
    if(recv(sockd, buffer, 34, 0) < 0){
    perror("Errore ricezione 226");
    onexit(sockd, 0, 0, 1);
  }
  printf("%s", buffer);
  memset(buffer, 0, sizeof(buffer));
  memset(dirpath, 0, sizeof(dirpath));
  free(sInfo.filebuffer);

The problem is that the string "226 File etc etc" is written inside the file that has been sent.
I've tried to do a small debug and so i've added a printf after the for loop (server sendfile) and a printf after the while loop (client) and i've noticed that the file is sent but on the client it doesn't exit from the while because the printf isn't printed...
Why i got this strange behaviour??
br>

EDIT:
The server send the file size to the client whit this code:

  fd = open(filename, O_RDONLY);
    if(fd < 0){
    error!!
    }

    if(fstat(fd, &fileStat) < 0){
        perror("Errore fstat");
        onexit(newsockd, sockd, fd, 3);
    }
    fsize = fileStat.st_size;
    if(send(newsockd, &fsize, sizeof(fsize), 0) < 0){
      perror("Errore durante l'invio della grandezza del file\n");
      onexit(newsockd, sockd, fd, 3);
     }

the client receives the fsize from the server with this code:

if(read(sockd, &fsize, sizeof(fsize)) < 0){
    perror("Errore durante ricezione grandezza file\n");
    onexit(sockd, 0 ,0 ,1);
}
fd = open(sInfo.filename, O_CREAT | O_WRONLY, 0644);
if (fd  < 0) {
    perror("open");
    onexit(sockd, 0 ,0 ,1);
}
fsize_tmp = fsize;

both fsize are declared as uint32_t...

Was it helpful?

Solution

Try this code:

Client side:

/* Client code */
/* TODO : Modify to meet your need */
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netinet/in.h>

#define PORT_NUMBER     5000
#define SERVER_ADDRESS  "192.168.1.7"
#define FILENAME        "/home/toc/foo.c"

int main(int argc, char **argv)
{
        int client_socket;
        ssize_t len;
        struct sockaddr_in remote_addr;
        char buffer[BUFSIZ];
        int file_size;
        FILE *received_file;
        int remain_data = 0;

        /* Zeroing remote_addr struct */
        memset(&remote_addr, 0, sizeof(remote_addr));

        /* Construct remote_addr struct */
        remote_addr.sin_family = AF_INET;
        inet_pton(AF_INET, SERVER_ADDRESS, &(remote_addr.sin_addr));
        remote_addr.sin_port = htons(PORT_NUMBER);

        /* Create client socket */
        client_socket = socket(AF_INET, SOCK_STREAM, 0);
        if (client_socket == -1)
        {
                fprintf(stderr, "Error creating socket --> %s\n", strerror(errno));

                exit(EXIT_FAILURE);
        }

        /* Connect to the server */
        if (connect(client_socket, (struct sockaddr *)&remote_addr, sizeof(struct sockaddr)) == -1)
        {
                fprintf(stderr, "Error on connect --> %s\n", strerror(errno));

                exit(EXIT_FAILURE);
        }

        /* Receiving file size */
        recv(client_socket, buffer, BUFSIZ, 0);
        file_size = atoi(buffer);
        //fprintf(stdout, "\nFile size : %d\n", file_size);

        received_file = fopen(FILENAME, "w");
        if (received_file == NULL)
        {
                fprintf(stderr, "Failed to open file foo --> %s\n", strerror(errno));

                exit(EXIT_FAILURE);
        }

        remain_data = file_size;

        while ((remain_data > 0) && ((len = recv(client_socket, buffer, BUFSIZ, 0)) > 0))
        {
                fwrite(buffer, sizeof(char), len, received_file);
                remain_data -= len;
                fprintf(stdout, "Receive %d bytes and we hope :- %d bytes\n", len, remain_data);
        }
        fclose(received_file);

        close(client_socket);

        return 0;
}

Server side:

/* Server code */
/* TODO : Modify to meet your need */
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/sendfile.h>

#define PORT_NUMBER     5000
#define SERVER_ADDRESS  "192.168.1.7"
#define FILE_TO_SEND    "hello.c"

int main(int argc, char **argv)
{
        int server_socket;
        int peer_socket;
        socklen_t       sock_len;
        ssize_t len;
        struct sockaddr_in      server_addr;
        struct sockaddr_in      peer_addr;
        int fd;
        int sent_bytes = 0;
        char file_size[256];
        struct stat file_stat;
        int offset;
        int remain_data;

        /* Create server socket */
        server_socket = socket(AF_INET, SOCK_STREAM, 0);
        if (server_socket == -1)
        {
                fprintf(stderr, "Error creating socket --> %s", strerror(errno));

                exit(EXIT_FAILURE);
        }

        /* Zeroing server_addr struct */
        memset(&server_addr, 0, sizeof(server_addr));
        /* Construct server_addr struct */
        server_addr.sin_family = AF_INET;
        inet_pton(AF_INET, SERVER_ADDRESS, &(server_addr.sin_addr));
        server_addr.sin_port = htons(PORT_NUMBER);

        /* Bind */
        if ((bind(server_socket, (struct sockaddr *)&server_addr, sizeof(struct sockaddr))) == -1)
        {
                fprintf(stderr, "Error on bind --> %s", strerror(errno));

                exit(EXIT_FAILURE);
        }

        /* Listening to incoming connections */
        if ((listen(server_socket, 5)) == -1)
        {
                fprintf(stderr, "Error on listen --> %s", strerror(errno));

                exit(EXIT_FAILURE);
        }

        fd = open(FILE_TO_SEND, O_RDONLY);
        if (fd == -1)
        {
                fprintf(stderr, "Error opening file --> %s", strerror(errno));

                exit(EXIT_FAILURE);
        }

        /* Get file stats */
        if (fstat(fd, &file_stat) < 0)
        {
                fprintf(stderr, "Error fstat --> %s", strerror(errno));

                exit(EXIT_FAILURE);
        }

        fprintf(stdout, "File Size: \n%d bytes\n", file_stat.st_size);

        sock_len = sizeof(struct sockaddr_in);
        /* Accepting incoming peers */
        peer_socket = accept(server_socket, (struct sockaddr *)&peer_addr, &sock_len);
        if (peer_socket == -1)
        {
                fprintf(stderr, "Error on accept --> %s", strerror(errno));

                exit(EXIT_FAILURE);
        }
        fprintf(stdout, "Accept peer --> %s\n", inet_ntoa(peer_addr.sin_addr));

        sprintf(file_size, "%d", file_stat.st_size);

        /* Sending file size */
        len = send(peer_socket, file_size, sizeof(file_size), 0);
        if (len < 0)
        {
              fprintf(stderr, "Error on sending greetings --> %s", strerror(errno));

              exit(EXIT_FAILURE);
        }

        fprintf(stdout, "Server sent %d bytes for the size\n", len);

        offset = 0;
        remain_data = file_stat.st_size;
        /* Sending file data */
        while (((sent_bytes = sendfile(peer_socket, fd, &offset, BUFSIZ)) > 0) && (remain_data > 0))
        {
                fprintf(stdout, "1. Server sent %d bytes from file's data, offset is now : %d and remaining data = %d\n", sent_bytes, offset, remain_data);
                remain_data -= sent_bytes;
                fprintf(stdout, "2. Server sent %d bytes from file's data, offset is now : %d and remaining data = %d\n", sent_bytes, offset, remain_data);
        }

        close(peer_socket);
        close(server_socket);

        return 0;
}

EDIT : Adding explanation from the man about the offset

The man page of send file said:

If offset is not NULL, then it points to a variable holding the file offset from which sendfile() will start reading data from in_fd.
When sendfile() returns, this variable will be set to the offset of the byte following the last byte that was read.

OTHER TIPS

The client doesn't know when the file ends. It just reads on until it has received fsize bytes. In your current implementaton the client will only work for files that are exactly fsize bytes.

I suggest that you change your protocol and add a header that contains the file size. Why not use the http protocol?

I think you should provide a more detailed code with at least the used variables declarations and initialisation.

How does the client part get the fsize value ?

You should also check the while condition like this :

while(((uint32_t)total_bytes_read < fsize) && ( .....

Don't use "!=", because if (for an unknown reason) total_bytes_read become greater than fsize, you'll be stucked in an infinite loop until the socket connection is closed (and read returns an error).

I also think (but not sure) that you should use recv instead of read in your client part.

You should always determine the size of file you're sending as part of the protocol, for example, you can send the file size as the first 4 (or more, depending on file sizes you expect to handle) bytes, prior to the actual stream.

If you wish to work with constant size files, your implementation should work, in this case please add prints for fsize and total_bytes_read.

You are using the sendfile API incorrectly. Since you are passing in a non-NULL value in the third parameter, sendfile will update the offset for you. But because your send loop is also updating the offset, you will be skipping over some bytes in the case that sendfile was not able to send the whole file in a single call.

From the man page:

If offset is not NULL, then it points to a variable holding the file offset from which sendfile() will start reading data from in_fd. When sendfile() returns, this variable will be set to the offset of the byte following the last byte that was read.

You should remove this line from your send loop:

  offset += rc;

Edit: In your update, you use fpl to get the fstat information from your file, but in your sendfile code, you use fd. I would make sure these are what you expect them to be. (This was fixed in yet another update.) In any case, I have written a test program using the code you provided (with my suggested fix), and it seems to work fine with a less than 2KB file and a 3MB file.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top