2010-06-26 15 views
5

Estoy intentando enviar archivos por sockets. Creé un programa y funciona para tipos de archivos como .cpp, .txt y otros archivos de texto. Pero los archivos binarios, las imágenes (.jpg, .png) y los archivos comprimidos como .zip y .rar no se envían correctamente. Sé que no tiene nada que ver con el tamaño de los archivos porque probé con archivos .txt grandes. No sé el problema, recibo todos los bytes que se envían pero el archivo no se puede abrir. La mayoría de las veces el archivo está dañado y no se puede ver. Busqué una solución en Google y encontré otras con el mismo problema y sin solución. Así que al ayudarme, también estás ayudando a cualquier otra persona que necesite una solución.Envío de imágenes a través de sockets de C++ (Linux)

código del servidor:

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

Código 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

¿Es esto realmente específico de Linux? – Wizard79

+0

@Lorenzo - Si no sabes cuál es el problema, ¿cómo sabrías por adelantado? Podrías preguntar razonablemente si está relacionado con C++. ¿Dónde trazamos la línea? – Duck

+0

Sin embargo, no encuentro ninguna buena razón para decir que este es un problema específico de Linux. Es probable que esté presente en todos los sistemas que proporcionan una interfaz de socket. – Wizard79

Respuesta

0

Para leer archivos de texto ASCII, un buffer char es aceptable.

Para leer datos binarios, necesitará usar unsigned char, de lo contrario sus datos se mancharán, ya que los datos binarios son bytes sin signo.

+0

oh thx intentaré eso – Warsame

+0

ahora estoy teniendo un problema tratando de comparar el búfer sin signo con "hola" para ver si debería finalizar la transferencia (lado del servidor) – Warsame

+0

Lo que significa que probablemente no sea la mejor manera de señalar el final de su transferencia de datos. Pregunta para pensar: ¿y si los datos que envía en realidad tienen "Hola" al final? – Alan

4

En primer lugar, debe intentar aumentar su búfer a algo más grande. Cuanto mayor sea el buffer, más eficiente será el transporte (simplemente no exageres y consumas demasiada memoria local).

En segundo lugar, debe comprobar y utilizar las longitudes devueltas por las funciones de lectura y escritura. En todas sus lecturas y escrituras solo verifica si se leyó o escribió algo, pero debe usar este recuento de bytes en su próxima operación de escritura/lectura. Por ejemplo, si el cliente informa que ha leído 1 byte, solo debe escribir 1 byte en el disco. Además, si tiene 1024 bytes leídos del servidor, debe intentar escribir 1024 bytes en el disco, lo que podría no suceder en esa llamada y es posible que necesite otra llamada a la escritura para completar la operación.

Sé que esto suena como un montón de trabajo, pero así es como se debe hacer para garantizar las operaciones de E/S. Todas las lecturas y escrituras tienen que hacerse básicamente dentro de sus propios bucles.

2

¿Sabe con certeza que su archivo de imagen binaria no tiene una secuencia de bytes 0x48 0x69 ("Hi") en él? Su ciclo de lectura terminará tan pronto como reciba esa secuencia. Además, llama al strcmp() con un búfer de caracteres de dos bytes de longitud y es casi seguro que garantiza que no tiene un byte de terminación nulo ('\0').

¿También podría investigar cómo difieren los archivos? La herramienta cmp puede darle una lista de bytes que son diferentes entre el origen y el destino.

Para la corrección, definitivamente desea comprobar los resultados de devolución de read(), write(), send(), y así sucesivamente. La posibilidad de lecturas cortas y escrituras con tomas es muy alta, por lo que es de vital importancia que su código sea capaz de manejar los casos en los que no se transfieren todos los datos. Como no realiza un seguimiento del número de bytes devueltos por recv(), es posible que solo haya recibido un solo byte, pero haya escrito dos bytes en la llamada write() que lo sigue.

Para el rendimiento, un búfer más grande ayudará a reducir la sobrecarga de las llamadas al sistema para mover los datos, aunque es aceptable realizar operaciones más pequeñas si el ancho de banda y la latencia no son preocupaciones principales. Y no deberían ser hasta que tengas un comportamiento correcto.

Por último, para compatibilidad con sistemas operativos menos iluminados, debe abrir los archivos en modo binario con "rb" y "wb" para que las nuevas líneas no se arruinen durante la escritura.

0

El problema es que está abriendo los archivos en el modo de texto con fopen(..., "r") y fopen(..., "w"). Debe utilizar el modo binario ("rb" y "wb") para archivos que no sean de texto.

+2

Si bien es importante para la compatibilidad con Windows y el antiguo Mac OS, eso no importa en las plataformas derivadas de Unix, como OS X o Linux (como decía el cartel que estaba usando). – Hudson

0

Otros señalado problemas con su código, a saber:

  • no usar valores de retorno de read(2) y write(2) llamadas,
  • mezclando datos binarios y datos de control de carácter,
  • utilizando strcmp(3) de cadenas que podrían no tener terminación cero (permítanme señalar que el uso de funciones que dependen de la terminación de cero en los datos recibidos de la red generalmente no es una buena idea, ya menudo conduce a sobrepasamientos del búfer).

Sería mucho mejor definir un protocolo simple para la transferencia de archivos (lea el "The ultimate SO_LINGER page, or: why is my tcp not reliable" si quiere saber por qué). Deje que el servidor sepa por adelantado la cantidad de datos que está enviando - preceda la transferencia con un encabezado de tamaño fijo que contenga la longitud del archivo (que también podría incluir el nombre del archivo, pero también necesitaría transferir la longitud de ese nombre). Preste atención al número endianness - envíe siempre números en network byte order.

Dado que está en Linux, permítame también señalarle sendfile(2), que es una forma muy eficiente de enviar archivos, ya que evita copiar datos en/desde un usuario.

Cuestiones relacionadas