2010-06-26 24 views
5

Sto tentando di inviare file tramite socket. Ho creato un programma e funziona per tipi di file come .cpp, .txt e altri file di testo. Ma file binari, immagini (.jpg, .png) e file compressi come .zip e .rar non vengono inviati correttamente. So che non è qualcosa a che fare con la dimensione dei file perché ho provato con file .txt di grandi dimensioni. Non conosco il problema, sto ricevendo tutti i byte inviati ma il file non può essere aperto. La maggior parte delle volte il file è corrotto e non può essere visualizzato. Ho cercato tramite Google una soluzione e ho appena trovato altri con lo stesso problema e nessuna soluzione. Quindi, aiutandomi, stai anche aiutando chiunque altro abbia bisogno di una soluzione.Invio di immagini tramite socket C++ (Linux)

Codice Server:

#include <stdlib.h> 
#include <stdio.h> 
#include <string.h> 
#include <sys/socket.h> 
#include <sys/types.h> 
#include <netinet/in.h> 
#include <arpa/inet.h> 
#include <unistd.h> 

int main (int agrc, char *argv[]) 
{ 
    /******** Program Variable Define & Initialize **********/ 
    int Main_Socket; // Main Socket For Server 
    int Communication_Socket; // Socket For Special Clients 
    int Status; // Status Of Function 
    struct sockaddr_in Server_Address; // Address Of Server 
    struct sockaddr_in Client_Address;// Address Of Client That Communicate with Server 
    int Port; 
    char Buff[100] = ""; 
    Port = atoi(argv[2]); 
    printf ("Server Communicating By Using Port %d\n", Port); 
    /******** Create A Socket To Communicate With Server **********/ 
    Main_Socket = socket (AF_INET, SOCK_STREAM, 0); 
    if (Main_Socket == -1) 
    { 
      printf ("Sorry System Can Not Create Socket!\n"); 
    } 
    /******** Create A Address For Server To Communicate **********/ 
    Server_Address.sin_family = AF_INET; 
    Server_Address.sin_port = htons(Port); 
    Server_Address.sin_addr.s_addr = inet_addr(argv[1]); 
    /******** Bind Address To Socket **********/ 
    Status = bind (Main_Socket, (struct sockaddr*)&Server_Address, sizeof(Server_Address)); 
    if (Status == -1) 
    { 
      printf ("Sorry System Can Not Bind Address to The Socket!\n"); 
    } 
    /******** Listen To The Port to Any Connection **********/   
    listen (Main_Socket,12);  
    socklen_t Lenght = sizeof (Client_Address); 
    while (1) 
    { 
     Communication_Socket = accept (Main_Socket, (struct sockaddr*)&Client_Address, &Lenght); 

     if (!fork()) 
     { 

      FILE *fp=fopen("recv.jpeg","w"); 
      while(1) 
      { 
       char Buffer[2]=""; 
       if (recv(Communication_Socket, Buffer, sizeof(Buffer), 0)) 
       { 
        if (strcmp (Buffer,"Hi") == 0 ) 
        { 
         break; 
        } 
        else 
        { 
         fwrite(Buffer,sizeof(Buffer),1, fp); 
        } 
       } 
      } 
      fclose(fp); 
      send(Communication_Socket, "ACK" ,3,0); 
      printf("ACK Send"); 
      exit(0); 
     } 
    } 
    return 0; 
} 

Codice Cliente:

#include <stdlib.h> 
#include <stdio.h> 
#include <string.h> 
#include <sys/socket.h> 
#include <sys/types.h> 
#include <netinet/in.h> 
#include <arpa/inet.h> 
#include <unistd.h> 

int main (int agrc, char *argv[]) 
{ 
    int Socket; 

    struct sockaddr_in Server_Address; 
    Socket = socket (AF_INET, SOCK_STREAM, 0); 
    if (Socket == -1) 
    { 
     printf ("Can Not Create A Socket!");  
    } 
    int Port ; 
    Port = atoi(argv[2]); 
    Server_Address.sin_family = AF_INET; 
    Server_Address.sin_port = htons (Port); 
    Server_Address.sin_addr.s_addr = inet_addr(argv[1]); 
    if (Server_Address.sin_addr.s_addr == INADDR_NONE) 
    { 
     printf ("Bad Address!"); 
    } 
    connect (Socket, (struct sockaddr *)&Server_Address, sizeof (Server_Address)); 


    FILE *in = fopen("background.jpeg","r"); 
    char Buffer[2] = ""; 
    int len; 
    while ((len = fread(Buffer,sizeof(Buffer),1, in)) > 0) 
    {    
     send(Socket,Buffer,sizeof(Buffer),0);    
    } 
    send(Socket,"Hi",sizeof(Buffer),0); 

    char Buf[BUFSIZ]; 
    recv(Socket, Buf, BUFSIZ, 0); 
    if (strcmp (Buf,"ACK") == 0 ) 
    { 
     printf("Recive ACK\n"); 
    }   
    close (Socket); 
    fclose(in); 
    return 0; 
} 
+0

È veramente specifico di Linux? – Wizard79

+0

@Lorenzo - Se non sai qual è il problema, come si dovrebbe sapere in anticipo? Si potrebbe ragionevolmente chiedere se è correlato al C++. Dove disegniamo la linea? – Duck

+0

Tuttavia, non ho trovato alcun motivo valido per dire che questo è un problema specifico di Linux. È probabilmente presente su ogni sistema che fornisce un'interfaccia socket. – Wizard79

risposta

0

Per la lettura di file di testo ASCII, un buffer char è accettabile.

Per la lettura di dati binari, è necessario utilizzare unsigned char, altrimenti i dati verranno recuperati, poiché i dati binari sono byte non firmati.

+0

oh thx provatelo ora – Warsame

+0

ora sto avendo un problema cercando di confrontare il char buffer senza segno con "ciao" per vedere se dovrei terminare il trasferimento (lato server) – Warsame

+0

Il che significa, questo probabilmente non è il modo migliore per segnalare la fine del tuo trasferimento di dati. Domanda a cui pensare: cosa succede se i dati che hai inviato in realtà hanno "Ciao" alla fine? – Alan

4

In primo luogo, si dovrebbe provare ad aumentare il buffer a qualcosa di più grande. Più grande è il buffer, più efficiente è il trasporto (basta non esagerare e consumare troppa memoria locale).

In secondo luogo, si dovrebbe verificare e utilizzare le lunghezze restituiti dal funzioni di lettura e scrittura. In tutte le tue letture e le tue scritture controlli solo se qualcosa è stato letto/scritto, ma dovresti usare questo conteggio dei byte sulla tua successiva operazione di scrittura/lettura. Ad esempio, se il client segnala di aver letto 1 byte, è necessario scrivere solo 1 byte su disco. Inoltre, se hai 1024 byte letti dal server, dovresti provare a scrivere 1024 byte sul disco, cosa che potrebbe non succedere in quella chiamata e potresti aver bisogno di un'altra chiamata alla scrittura per completare l'operazione.

So che sembra molto lavoro, ma è così che deve essere fatto per garantire operazioni I/O. Tutte le letture e le scritture devono essere fatte essenzialmente all'interno dei propri loop.

2

Sapete per certo che il file di immagine binaria non ha una sequenza di byte 0x48 0x69 ("Hi")? Il tuo ciclo letto terminerà non appena riceverà quella sequenza. Inoltre, si chiama strcmp() con un buffer di caratteri che è lungo due byte e quasi certamente garantisce che non ha byte null di terminazione ('\0').

Si potrebbe anche indagare su come i file differiscono? Lo strumento cmp può fornire un elenco di byte diversi tra l'origine e la destinazione.

Per correttezza, si desidera verificare i risultati di ritorno da read(), write(), send() e così via. La possibilità di brevi letture e scritture con socket è molto alta, quindi è di vitale importanza che il tuo codice sia in grado di gestire i casi in cui non tutti i dati vengono trasferiti. Dal momento che non si traccia il numero di byte restituiti da recv(), è possibile che hai ricevuto solo un singolo byte, ma ha scritto due byte nella chiamata write() segue.

Per prestazioni un buffer più ampio consente di ridurre il sovraccarico delle chiamate di sistema per lo spostamento dei dati, sebbene sia accettabile eseguire operazioni più piccole se la larghezza di banda e la latenza non sono problemi primari. E non dovrebbero esserlo finché non avrai un comportamento corretto.

Infine, per la compatibilità con sistemi operativi meno illuminati, è necessario aprire i file in modalità binaria con "rb" e "wb" in modo che le righe non vengano storpiate durante la scrittura.

0

Il problema è che si stanno aprendo i file in modalità con fopen(..., "r") e fopen(..., "w"). È necessario utilizzare la modalità binaria ("rb" e "wb") per i file non di testo.

+2

Anche se è importante per la compatibilità con Windows e il vecchio Mac OS, ciò non importa su piattaforme derivate da Unix come OS X o Linux (come diceva il poster che stava usando). – Hudson

0

altri hanno sottolineato i problemi con il tuo codice, vale a dire:

  • non utilizzando i valori di ritorno di read(2) e write(2) chiamate,
  • miscelazione dati binari e dati di controllo di carattere,
  • utilizzando strcmp(3) sulle stringhe che potrebbero non essere a terminazione zero (notiamo che l'utilizzo di funzioni che si basano sulla terminazione dello zero sui dati ricevuti dalla rete non è generalmente una buona idea e spesso porta a sovraccarichi del buffer.)

Sarebbe molto meglio definire un semplice protocollo per il trasferimento di file (leggi lo "The ultimate SO_LINGER page, or: why is my tcp not reliable" se vuoi sapere perché). Fai sapere al server in anticipo la quantità di dati che stai inviando, prima del trasferimento con un'intestazione di dimensione fissa contenente la lunghezza del file (che potrebbe includere anche il nome del file, ma in tal caso sarà necessario trasferire anche la lunghezza di quel nome). Prestare attenzione al numero endianness - inviare sempre i numeri in network byte order.

Dato che ci si trova su Linux, lascia che punti anche a sendfile(2), che è un modo molto efficiente di inviare file poiché evita di copiare i dati da/verso l'utente.