C send and receive file

This is part of the server (sendfile):

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 part of the client part (recv file):

  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 line "226 File, etc." written inside the sent file.
I tried to do a little debugging, so I added printf after the for (server sendfile) loop and printf after the while (client) loop, and I noticed that the file was sent, but on the client it does not exit from this time because printf not printing. ..
Why did I get this strange behavior?
br>

EDIT:
The server sends the file size to the client with 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 fsize from the server using 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 ...

+6
source share
5 answers

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 (((len = recv(client_socket, buffer, BUFSIZ, 0)) > 0) && (remain_data > 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 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 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 a human explanation about offset

Send page man page:

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

+8
source

The client does not know when the file will end. It is simply read until it receives the fsize bytes. In your current implementation, the client will only work for files that exactly match fsize bytes.

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

0
source

I think you should provide more detailed code, at least with declarations and initialization of the variables used.

How does part of the client get fsize?

You should also check the while condition as follows:

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

Do not use " ! = " Because if (for some unknown reason) total_bytes_read becomes larger than fsize, you are stuck 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 reading in your client side.

0
source

You should always determine the size of the file that you send as part of the protocol, for example, you can send the file size as the first 4 (or more, depending on the size of the files you expect to process) bytes preceding the actual stream.

If you want to work with files with a constant size, your implementation should work, in this case add fingerprints for fsize and total_bytes_read.

0
source

You are using the sendfile API incorrectly. Since you are passing a non-NULL value in the third parameter, sendfile will update the offset for you. But since your send loop also updates the offset, you will skip some bytes if sendfile unable to send the whole file in one call.

On the man page:

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

You should remove this line from the send loop:

  offset += rc; 

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

0
source

Source: https://habr.com/ru/post/922832/


All Articles