Ceci est la partie serveur (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));
et ceci est la partie de la partie client (fichier recv):
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);
Le problème est que la chaîne "226 File etc etc" est écrite inside le fichier qui a été envoyé.
J'ai essayé de faire un petit débogage et donc j'ai ajouté un printf
après la boucle for (serveur sendfile) et un printf
après la boucle while (client) et j'ai remarqué que le fichier est envoyé mais en marche le client ne le quitte pas car la printf
n'est pas imprimée ...
Pourquoi j'ai eu ce comportement étrange ??
br>
MODIFIER:
Le serveur envoie la taille du fichier au client avec ce 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);
}
le client reçoit le fsize du serveur avec ce 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;
les deux fsize
sont déclarés en tant que uint32_t
...
Essayez ce code:
Côté client:
/* 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;
}
Du côté serveur:
/* 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: Ajout de l'explication de l'homme à propos de offset
La page de manuel de send file disait:
Si offset n'est pas NULL, il pointe sur une variable contenant le fichier offset à partir duquel sendfile () commencera à lire les données de in_fd.
Lorsque sendfile () est renvoyé, cette variable sera définie sur le décalage de l'octet suivant le dernier octet lu .
Vous utilisez l'API sendfile
de manière incorrecte. Puisque vous transmettez une valeur non NULL dans le troisième paramètre, sendfile
mettra à jour le décalage pour vous. Mais comme votre boucle d’envoi met également à jour le décalage, vous sauterez quelques octets si sendfile
n’a pas été en mesure d’envoyer le fichier entier en un seul appel.
De la page de manuel :
Si
offset
n'est pas NULL, il pointe vers une variable contenant le décalage de fichier à partir duquelsendfile()
commencera à lire les données à partir dein_fd
. Lorsquesendfile()
est retourné, cette variable sera définie sur le décalage de l'octet suivant le dernier octet lu.
Vous devriez supprimer cette ligne de votre boucle d’envoi:
offset += rc;
Modifier: Dans votre mise à jour, vous utilisez (Cela a été corrigé dans une autre mise à jour.) Dans tous les cas, j’ai écrit un programme test en utilisant le code que vous avez fourni (avec le correctif que vous avez suggéré) , et cela semble fonctionner correctement avec un fichier inférieur à 2 Ko et Fichier de 3 Mo.fpl
pour obtenir les informations fstat
de votre fichier, mais dans votre code sendfile
, vous utilisez fd
. Je voudrais m'assurer que ce sont ce que vous attendez d'eux.
Vous devez toujours déterminer la taille du fichier que vous envoyez dans le cadre du protocole. Par exemple, vous pouvez envoyer la taille du fichier sous la forme des 4 premiers octets (ou plus, en fonction de la taille du fichier que vous comptez gérer), avant les octets réels. courant.
Si vous souhaitez utiliser des fichiers de taille constante, votre implémentation devrait fonctionner. Dans ce cas, ajoutez des impressions pour fsize et total_bytes_read.
Le client ne sait pas quand le fichier se termine. Il reste à lire jusqu'à ce qu'il ait reçu fsize
octets. Dans votre implémentation actuelle, le client ne fonctionnera que pour les fichiers qui ont exactement fsize
octets.
Je vous suggère de modifier votre protocole et d'ajouter un en-tête contenant la taille du fichier ..___ Pourquoi ne pas utiliser le protocole http?
Je pense que vous devriez fournir un code plus détaillé avec au moins les déclarations de variables utilisées et l’initialisation.
Comment la partie client obtient-elle la valeur fsize?
Vous devriez également vérifier la condition while comme ceci:
while(((uint32_t)total_bytes_read < fsize) && ( .....
N'utilisez pas "! = ", car si (pour une raison inconnue) total_bytes_read devient supérieur à fsize, vous serez bloqué dans une boucle infinie jusqu'à ce que la connexion de socket soit fermée (et read renvoie une erreur).
Je pense aussi (mais je ne suis pas sûr) que vous devriez utiliser recv au lieu de read dans votre partie client.