2013-03-16 21 views
7

Sto scrivendo un piccolo programma C per poter trasferire un file immagine tra due computer (da server a client entrambi in esecuzione Linux) usando socket TCP/IP ma lì sembra essere un errore in quanto la mia immagine appare danneggiata negli altri lati.Invio immagine (JPEG) tramite Socket in C Linux

Il codice per il mio server è in quanto tale:

#include<stdio.h> 
#include<string.h> 
#include<sys/socket.h> 
#include<arpa/inet.h> 
#include<unistd.h> 
#include<iostream> 
#include<fstream> 
#include<errno.h> 

using namespace std; 

int send_image(int socket){ 

FILE *picture; 
int size, read_size; 
char send_buffer[10240], verify; 

picture = fopen("2.jpg", "r"); 
printf("Getting Picture Size\n"); 

if(picture == NULL) { 
    printf("Error Opening Image File"); 
} 

fseek(picture, 0, SEEK_END); 
size = ftell(picture); 
fseek(picture, 0, SEEK_SET); 

//Send Picture Size 
printf("Sending Picture Size\n"); 
write(socket, (void *)&size, sizeof(int)); 

if(read_size = read(socket, &verify , sizeof(char)) < 0) { 
    puts("\nError Receiving Verification"); 
} 


if(verify == '1'){ 
    printf("5\n"); 
    //Send Picture as Byte Array 
    printf("Sending Picture as Byte Array\n"); 

    while(!feof(picture)) { 

      //Read from the file into our send buffer 
      read_size = fread(send_buffer, 1, sizeof(send_buffer)-1, picture); 

      //Send data through our socket 
      write(socket, send_buffer, read_size);       

      //Wait for the verify signal to be received 
      while(read(socket, &verify , sizeof(char)) < 0); 

      if(verify != '1') { 
      printf("Error Receiving the Handshake signal\n %s",&verify); 
      } 

      verify = ''; 

      //Zero out our send buffer 
      bzero(send_buffer, sizeof(send_buffer)); 
    } 
} 
} 

int main(int argc , char *argv[]) 
{ 
int socket_desc , new_socket , c, read_size,buffer = 0; 
struct sockaddr_in server , client; 
char *readin; 

//Create socket 
socket_desc = socket(AF_INET , SOCK_STREAM , 0); 
if (socket_desc == -1) 
{ 
    printf("Could not create socket"); 
} 

//Prepare the sockaddr_in structure 
server.sin_family = AF_INET; 
server.sin_addr.s_addr = INADDR_ANY; 
server.sin_port = htons(8889); 

//Bind 
if(bind(socket_desc,(struct sockaddr *)&server , sizeof(server)) < 0) 
{ 
    puts("bind failed"); 
    return 1; 
} 

puts("bind done"); 

//Listen 
listen(socket_desc , 3); 

//Accept and incoming connection 
puts("Waiting for incoming connections..."); 
c = sizeof(struct sockaddr_in); 

if((new_socket = accept(socket_desc, (struct sockaddr *)&client, (socklen_t*)&c))){ 
    puts("Connection accepted"); 
} 

fflush(stdout); 

if (new_socket<0) 
{ 
    perror("Accept Failed"); 
    return 1; 
} 

send_image(new_socket); 

    close(socket_desc); 
    fflush(stdout); 
return 0; 
} 

Il codice lato client che sta ricevendo i dati sono in quanto tale:

#include<stdio.h> 
#include<string.h> //strlen 
#include<sys/socket.h> 
#include<sys/ioctl.h> 
#include<arpa/inet.h>  
#include<unistd.h> 
#include<iostream> 
#include<errno.h> 
using namespace std; 

//This function is to be used once we have confirmed that an image is to be sent 
//It should read and output an image file 
int receive_image(int socket){ 

int buffersize = 0, recv_size = 0,size = 0, read_size, write_size; 
char imagearray[10241],verify = '1'; 
FILE *image; 

//Find the size of the image 
read(socket, &size, sizeof(int)); 



//Send our verification signal 
write(socket, &verify, sizeof(char)); 
//Make sure that the size is bigger than 0 
if(size <= 0){ 
    printf("Error has occurred. Size less than or equal to 0\n"); 
    return -1; 
} 

image = fopen("2.jpg", "w"); 

if(image == NULL) { 
    printf("Error has occurred. Image file could not be opened\n"); 
    return -1; 
} 

//Loop while we have not received the entire file yet 
while(recv_size < size) { 
    ioctl(socket, FIONREAD, &buffersize); 

    //We check to see if there is data to be read from the socket  
    if(buffersize > 0) { 

     if(read_size = read(socket,imagearray, buffersize) < 0){ 
      printf("%s", strerror(errno)); 
     } 

     //Write the currently read data into our image file 
     write_size = fwrite(imagearray,1,(buffersize), image); 

     if(write_size != buffersize) { 
      printf("write and buffersizes wrong"); 
     } 

     if(read_size !=write_size) { 
      printf("error in read write"); 
     } 

     //Increment the total number of bytes read 
     recv_size += read_size; 

        //Send our handshake verification info 
     write(socket, &verify, sizeof(char)); 

    } 
} 

fclose(image); 
printf("Image successfully Received!\n"); 
return 1; 
} 

int main(int argc , char *argv[]) 
{ 

int socket_desc; 
struct sockaddr_in server; 
char *parray; 


//Create socket 
socket_desc = socket(AF_INET , SOCK_STREAM , 0); 

if (socket_desc == -1) { 
    printf("Could not create socket"); 
} 

memset(&server,0,sizeof(server)); 
server.sin_addr.s_addr = inet_addr("10.42.0.1"); 
server.sin_family = AF_INET; 
server.sin_port = htons(8889); 

//Connect to remote server 
if (connect(socket_desc , (struct sockaddr *)&server , sizeof(server)) < 0) { 
    cout<<strerror(errno); 
    close(socket_desc); 
    puts("Connect Error"); 
    return 1; 
} 

puts("Connected\n"); 

receive_image(socket_desc); 

close(socket_desc); 

return 0; 
} 

Qualcuno può darmi una mano con questo? Non riesco a vedere per capire questo errore per la vita di me.

EDIT: ho cambiato le fwrites e freads indietro nella scrittura regolare e leggere e invia comunque un immagine corrotta

+1

Non hai bisogno di '' ioctl' e FIONREAD'; lasciatelo bloccare se non ci sono ancora dati. Non è che tu stia facendo qualcos'altro nel frattempo. – icktoofay

+0

Hai confermato che ogni singolo read_size corrisponde a ogni singolo write_size? –

+0

Possibile duplicato di [Invia e ricevi un file in programmazione socket in Linux con C/C++ (GCC/G ++)] (http://stackoverflow.com/questions/2014033/send-and-receive-a-file-in- programmazione socket in-linux-con-cc-gcc-g) –

risposta

6

Si dispone di un certo numero di problemi:

  • È necessario aprire il file in modalità binaria ("rb" per la lettura, "wb" per la scrittura), non la modalità testo predefinita. Su Windows (e su qualsiasi altro sistema che esegue la conversione di fine riga), la libreria stdio converte LF (byte 0x0A) in coppie CRLF (i due byte 0x0D 0x0A) durante la scrittura e esegue la traduzione inversa durante la lettura. Per i dati non di testo come i file JPEG, questo corrompe i dati.
  • Non è necessario inviare i byte di "handshake" dopo ogni invio. TCP/IP gestisce già riconoscimenti/resends/controllo di flusso/ecc. Si può supporre che finchè send()/write() restituisce un valore positivo, quindi molti byte sono stati ricevuti dall'altro peer.
  • send()/write() non può inviare tutti i dati che gli chiedi di fare un invio parziale. Se ciò accade, è necessario continuare a provare a inviare il resto del buffer in un ciclo.
  • sizeof(char) è garantito per essere 1 dallo standard del linguaggio C, raramente c'è un bisogno di dire sizeof(char) quando invece il codice sarà molto più chiaro senza di essa
  • nel codice client, non c'è bisogno di usare quel ioctl per determinare come molti dati possono essere letti senza bloccare perché stai ancora ricominciando: il tuo codice ruoterà al 100% della CPU mentre non ci sono dati disponibili. Lasciare semplicemente il call block read(). Se stai utilizzando questo codice su un laptop, la batteria ti ringrazierà.
  • Allo stesso modo, il client otterrà quasi sicuramente letture parziali, non si riceverà l'intero file in una singola chiamata. È necessario scrivere tutti i dati che si ottengono, quindi ripetere e ricevere di nuovo.
  • Quando si inviano le dimensioni dell'immagine sul socket all'inizio, è possibile ottenere un valore diverso sul client se i due sistemi non sono dello stesso tipo. Per rendere il tuo codice a prova di proiettile, è necessario convertire i dati in ordine di rete (big-endian) al momento dell'invio, quindi convertirlo nuovamente in ordine host (nativo) dopo averlo ricevuto. È possibile utilizzare le funzioni ntohl(3) and htonl(3) per eseguire queste conversioni per valori a 4 byte.
+0

Penso che sia sicuro dire se si '# include ', quindi non è necessario aprire i file con '" rb "' e '" r "' va bene. –

+0

Per la parte client del codice in cui hai menzionato si stanno ottenendo letture parziali, il mio codice non si sta già occupando di questo? Ho letto quanti più byte sono disponibili e poi scrivo sul file il numero di byte restituiti dalla funzione di lettura e quindi il ciclo fino a quando non ha scritto il numero di byte uguale alla dimensione del file. – user1721182

+0

Anche questo programma di trasferimento di immagini viene utilizzato solo per un client e un server che eseguono entrambi Ubuntu, quindi non è necessario l'apertura del file in modalità binaria? – user1721182

4

Funziona bene ora.

Best,

Mario.

Cliente:

#include<stdio.h> 
#include<string.h> 
#include<sys/socket.h> 
#include<arpa/inet.h> 
#include<sys/ioctl.h> 
#include<unistd.h> 
#include<iostream> 
#include<fstream> 
#include<errno.h> 
using namespace std; 

//This function is to be used once we have confirmed that an image is to be sent 
//It should read and output an image file 

int receive_image(int socket) 
{ // Start function 

int buffersize = 0, recv_size = 0,size = 0, read_size, write_size, packet_index =1,stat; 

char imagearray[10241],verify = '1'; 
FILE *image; 

//Find the size of the image 
do{ 
stat = read(socket, &size, sizeof(int)); 
}while(stat<0); 

printf("Packet received.\n"); 
printf("Packet size: %i\n",stat); 
printf("Image size: %i\n",size); 
printf(" \n"); 

char buffer[] = "Got it"; 

//Send our verification signal 
do{ 
stat = write(socket, &buffer, sizeof(int)); 
}while(stat<0); 

printf("Reply sent\n"); 
printf(" \n"); 

image = fopen("capture2.jpeg", "w"); 

if(image == NULL) { 
printf("Error has occurred. Image file could not be opened\n"); 
return -1; } 

//Loop while we have not received the entire file yet 


int need_exit = 0; 
struct timeval timeout = {10,0}; 

fd_set fds; 
int buffer_fd, buffer_out; 

while(recv_size < size) { 
//while(packet_index < 2){ 

    FD_ZERO(&fds); 
    FD_SET(socket,&fds); 

    buffer_fd = select(FD_SETSIZE,&fds,NULL,NULL,&timeout); 

    if (buffer_fd < 0) 
     printf("error: bad file descriptor set.\n"); 

    if (buffer_fd == 0) 
     printf("error: buffer read timeout expired.\n"); 

    if (buffer_fd > 0) 
    { 
     do{ 
       read_size = read(socket,imagearray, 10241); 
      }while(read_size <0); 

      printf("Packet number received: %i\n",packet_index); 
     printf("Packet size: %i\n",read_size); 


     //Write the currently read data into our image file 
     write_size = fwrite(imagearray,1,read_size, image); 
     printf("Written image size: %i\n",write_size); 

      if(read_size !=write_size) { 
       printf("error in read write\n"); } 


      //Increment the total number of bytes read 
      recv_size += read_size; 
      packet_index++; 
      printf("Total received image size: %i\n",recv_size); 
      printf(" \n"); 
      printf(" \n"); 
    } 

} 


    fclose(image); 
    printf("Image successfully Received!\n"); 
    return 1; 
    } 

    int main(int argc , char *argv[]) 
    { 

    int socket_desc; 
    struct sockaddr_in server; 
    char *parray; 


    //Create socket 
    socket_desc = socket(AF_INET , SOCK_STREAM , 0); 

    if (socket_desc == -1) { 
    printf("Could not create socket"); 
    } 

    memset(&server,0,sizeof(server)); 
    server.sin_addr.s_addr = inet_addr("10.0.0.30"); 
    server.sin_family = AF_INET; 
    server.sin_port = htons(8889); 

    //Connect to remote server 
    if (connect(socket_desc , (struct sockaddr *)&server , sizeof(server)) < 0) { 
    cout<<strerror(errno); 
    close(socket_desc); 
    puts("Connect Error"); 
    return 1; 
    } 

    puts("Connected\n"); 

    receive_image(socket_desc); 

    close(socket_desc); 

    return 0; 
    } 

Server:

#include<stdio.h> 
    #include<string.h> 
    #include<sys/socket.h> 
    #include<arpa/inet.h> 
    #include<unistd.h> 
    #include<iostream> 
    #include<fstream> 
    #include<errno.h> 

    using namespace std; 

    int send_image(int socket){ 

    FILE *picture; 
    int size, read_size, stat, packet_index; 
    char send_buffer[10240], read_buffer[256]; 
    packet_index = 1; 

    picture = fopen("capture.jpeg", "r"); 
    printf("Getting Picture Size\n"); 

    if(picture == NULL) { 
     printf("Error Opening Image File"); } 

    fseek(picture, 0, SEEK_END); 
    size = ftell(picture); 
    fseek(picture, 0, SEEK_SET); 
    printf("Total Picture size: %i\n",size); 

    //Send Picture Size 
    printf("Sending Picture Size\n"); 
    write(socket, (void *)&size, sizeof(int)); 

    //Send Picture as Byte Array 
    printf("Sending Picture as Byte Array\n"); 

    do { //Read while we get errors that are due to signals. 
     stat=read(socket, &read_buffer , 255); 
     printf("Bytes read: %i\n",stat); 
    } while (stat < 0); 

    printf("Received data in socket\n"); 
    printf("Socket data: %c\n", read_buffer); 

    while(!feof(picture)) { 
    //while(packet_index = 1){ 
     //Read from the file into our send buffer 
     read_size = fread(send_buffer, 1, sizeof(send_buffer)-1, picture); 

     //Send data through our socket 
     do{ 
     stat = write(socket, send_buffer, read_size); 
     }while (stat < 0); 

     printf("Packet Number: %i\n",packet_index); 
     printf("Packet Size Sent: %i\n",read_size);  
     printf(" \n"); 
     printf(" \n"); 


     packet_index++; 

     //Zero out our send buffer 
     bzero(send_buffer, sizeof(send_buffer)); 
    } 
    } 

    int main(int argc , char *argv[]) 
    { 
     int socket_desc , new_socket , c, read_size,buffer = 0; 
     struct sockaddr_in server , client; 
     char *readin; 

     //Create socket 
     socket_desc = socket(AF_INET , SOCK_STREAM , 0); 
     if (socket_desc == -1) 
     { 
     printf("Could not create socket"); 
     } 

     //Prepare the sockaddr_in structure 
     server.sin_family = AF_INET; 
     server.sin_addr.s_addr = INADDR_ANY; 
     server.sin_port = htons(8889); 

     //Bind 
    if(bind(socket_desc,(struct sockaddr *)&server , sizeof(server)) < 0) 
    { 
     puts("bind failed"); 
     return 1; 
    } 

    puts("bind done"); 

    //Listen 
    listen(socket_desc , 3); 

     //Accept and incoming connection 
     puts("Waiting for incoming connections..."); 
     c = sizeof(struct sockaddr_in); 

    if((new_socket = accept(socket_desc, (struct sockaddr *)&client,(socklen_t*)&c))){ 
puts("Connection accepted"); 
     } 

    fflush(stdout); 

    if (new_socket<0) 
    { 
     perror("Accept Failed"); 
     return 1; 
    } 

    send_image(new_socket); 

    close(socket_desc); 
    fflush(stdout); 
    return 0; 
    }