2010-01-29 6 views
2

J'essaie d'envoyer un fichier à un socket particulier en utilisant C; voici le code:Envoyer un fichier à un socket

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; 
} 

Quand je lance ce programme et essayer de voir le fichier texte dans Firefox à http://127.0.0.1:8080/, une partie du fichier est coupé de la fin si la taille du fichier est de plus de 2000 octets. Si j'envoie une photo, seulement les 3/4 de l'image sont chargés (coupés par le bas).

La fonction renvoie toujours 0 à l'appelant. Où le dernier bloc d'octets qu'il envoie() disparaît-il? Dois-je vider un flux avant de revenir?

Merci

EDIT: Ce est un extrait de ma fonction principale():

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

Êtes-vous sûr que le chemin « échec » ne se produit pas ? Vous devriez imprimer un message d'erreur à l'erreur standard à ce point, plutôt que de simplement retourner -1. –

+0

Oui, j'ai vérifié la valeur qu'il retourne à main, et c'est toujours 0. – io555782

+0

Pouvez-vous compiler le code avec les avertissements maximum et afficher les avertissements? Avez-vous inclus stdio.h, stdlib.h et sys/socket.h? –

Répondre

1

Il y a une API standard pour cela! Utilisez sendfile().

Le noyau effectue la copie sans changement de contexte dans votre thread, ce qui est nettement plus efficace.

+1

Ce n'est pas standard du tout. Selon http://linux.die.net/man/2/sendfile - "Non spécifié dans POSIX.1-2001, ou d'autres standards.Les autres systèmes Unix implémentent sendfile() avec des sémantiques et des prototypes différents. dans les programmes portables. " –

1

Vous ne devriez jamais utiliser feof(), c'est presque toujours la mauvaise chose à faire. Au lieu de cela, utilisez la valeur de retour de fread() pour déterminer quand vous avez tout lu - tant que ce n'est pas zéro, vous devez continuer à lire et à envoyer. Dans le code pseudo:

while(1) { 
    r = fread(...); 
    if (r == 0) { 
     break; 
    } 
    send(..); 
} 
+0

Code modifié, toujours le même problème ... le texte que je vois dans mon navigateur est coupé à un moment donné. : | – io555782

+0

@ io555782 Le code que vous avez posté ne fonctionnera pas tant que vous utilisez eof() –

+0

Je l'ai modifié comme vous l'avez dit (sans feof()) http://pastebin.com/f29a656b0; encore coupe du texte à la fin. – io555782

0

Je ne vois pas le reste de votre programme, mais je soupçonne que vous quittez avant de fermer votre prise.

Si votre programme se termine avant que le socket ait fini d'envoyer ses données, le système d'exploitation n'est pas obligé de faire quoi que ce soit avec les données non envoyées restantes et le rejettera probablement.

0

Le code que vous avez posté me semble correct. Le problème de feof peut exister comme le dit Neil, même si cela fonctionne très bien en utilisant le compilateur de Microsoft. Je l'ai testé comme écrit avec un talon pour l'appel d'envoi, et toutes les données ont été "envoyées". Il me semble que l'erreur réside peut-être à la fin de la réception. Un test simple consisterait simplement à imprimer les longueurs de fichier que vous envoyez et à lire aux deux extrémités. Assurez-vous que l'appel recv obtient toutes les données vidées.

+0

Merci. L'extrémité de réception que j'ai utilisée pour tester ce code est un navigateur Web, pas un programme écrit par moi. – io555782

+0

Ah - à droite. Vous mentionnez cela dans votre message et j'ai rapidement oublié. Je pense que Zan pourrait être sur quelque chose. –

0

Si vous regardez le fichier avec un navigateur Web, vous devriez envoyer un en-tête HTTP avant d'envoyer les données réelles.

Je ne fais que deviner, mais très probablement, dans l'en-tête, vous envoyez la taille des données à 2000 octets. Ainsi, même si vous envoyez plus de 2000 octets, le navigateur ne les attend pas et n'affiche que les 2000 premiers octets.

En outre, puisque vous fermez la connexion, vous should send Connection: close header aux clients.

0

Etes-vous en lisant toutes les données que le navigateur vous a envoyé? Si vous n'êtes pas (ce qui est compréhensible, si vous n'êtes pas en train d'écrire un serveur web), alors appeler close() avec des données non lues provoque un abandon sur la connexion TCP au lieu d'un arrêt propre.

Un arrêt correct attend l'envoi de toutes les données en attente; un abandon ne fonctionne pas, ce qui peut entraîner la perte de données en attente.

Pour des fins de test, vous devriez être en mesure de le réparer en mettant ce à la fin de la fonction d'envoi (avant libre 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) */ 
Questions connexes