Отправка изображений по C++ снабжает (Linux) сокетом

Я пытаюсь отправить файлы по сокетам. Я создал программу, и она работает на типы файлов, такие как .cpp, .txt, и другие текстовые файлы. Но двоичные файлы, изображения (.jpg, .png) и сжатые файлы, такие как .zip и .rar не отправляются правильно. Я знаю, что это не что-то, чтобы сделать с размером файлов, потому что я протестировал с большими .txt файлами. Я не знаю проблемы, я получаю все отправляемые байты, но файл не может быть открыт. Большинство раз файл поврежден и не может быть просмотрен. Я перерыл Google для решения и просто нашел других с той же проблемой и никаким решением. Таким образом путем помощи мне Вы также помогаете кому-либо еще нуждающемуся в решении.

Серверный код:

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

Клиентский код:

#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;   
}
6
задан Benjamin 9 December 2013 в 12:26
поделиться

5 ответов

Во-первых, вы должны попробовать увеличить буфер до чего-то большего. Чем больше буфер, тем эффективнее транспорт (только не преувеличивайте и не потребляйте слишком много локальной памяти).

Во-вторых, вы должны проверить, что и используют длины, возвращаемые функциями чтения и записи. Во всех ваших операциях чтения и записи вы только проверяете, было ли что-то прочитано / записано, но вы должны использовать это количество байтов при следующей операции записи / чтения. Например, если клиент сообщает, что он прочитал 1 байт, вы должны записать на диск только 1 байт. Кроме того, если у вас есть 1024 байта, прочитанные с сервера, вы должны попытаться записать 1024 байта на диск, что может не произойти в этом вызове, и вам может потребоваться еще один вызов записи для завершения операции.

Я знаю, что это звучит как большая работа, но именно так это нужно делать, чтобы гарантировать операции ввода-вывода. Все операции чтения и записи в основном должны выполняться внутри их собственных циклов.

4
ответ дан 17 December 2019 в 02:21
поделиться

Для чтения текстовых файлов ascii допустим буфер char .

Для чтения двоичных данных вам нужно будет использовать unsigned char , иначе ваши данные будут сброшены, поскольку двоичные данные представляют собой байты без знака.

0
ответ дан 17 December 2019 в 02:21
поделиться

Проблема в том, что вы открываете файлы в режиме text с помощью fopen (..., "r") и fopen (..., "ш") . Вам необходимо использовать двоичный режим ( «rb» и «wb» ) для нетекстовых файлов.

0
ответ дан 17 December 2019 в 02:21
поделиться

Знаете ли вы, что в вашем двоичном файле образа нет последовательности байтов 0x48 0x69 ( «Привет» )? Ваш цикл чтения завершится, как только он получит эту последовательность. Кроме того, вы вызываете strcmp () с символьным буфером длиной два байта и почти наверняка гарантируете, что он не имеет нулевого завершающего байта ( '\ 0' ).

Вы также можете изучить, чем файлы различаются? Инструмент cmp может предоставить вам список байтов, которые различаются между источником и местом назначения.

Для правильности вы определенно хотите проверить результаты, возвращаемые read () , write () , send () и так далее. Вероятность коротких операций чтения и записи с помощью сокетов очень высока, поэтому жизненно важно, чтобы ваш код мог обрабатывать случаи, когда не все данные передаются. Поскольку вы не отслеживаете, сколько байтов возвращает recv () , возможно, что вы получили только один байт, но записали два байта в вызове write () после Это.

Для повышения производительности больший буфер поможет снизить накладные расходы на системные вызовы для перемещения данных, хотя допустимо выполнение меньших операций, если полоса пропускания и задержка не являются первоочередными задачами. И этого не должно быть, пока вы не научитесь правильно вести себя.

Наконец, для совместимости с менее продвинутыми операционными системами вы должны открывать файлы в двоичном режиме с помощью «rb» и «wb» , чтобы символы новой строки не искажались во время записи.

2
ответ дан 17 December 2019 в 02:21
поделиться

Другие указали на проблемы с вашим кодом, а именно:

  • не используют возвращаемые значения вызовов read (2) и write (2) ,
  • смешивание двоичных данных и символьных управляющих данных,
  • использование strcmp (3) для строк, которые могут не оканчиваться нулем (позвольте мне отметить, что использование функций, полагающихся на завершение нуля для данных, полученных от сеть, как правило, не является хорошей идеей и часто приводит к переполнению буфера.)

Вам было бы намного лучше определить простой протокол для передачи файлов (прочтите "Окончательную страницу SO_LINGER, или: почему мой tcp ненадежно », если вы хотите знать, почему.) Сообщите стороне сервера заранее, сколько данных вы отправляете - перед передачей укажите заголовок фиксированного размера, содержащий длину файла (который также может включать имя файла, но тогда вам нужно будет передать и длину этого имени.) Обратите внимание на порядковый номер - всегда отправляйте номера в сети порядок байтов .

Поскольку вы работаете в Linux, позвольте мне также указать вам на sendfile (2) , который является очень эффективным способом отправки файлов, поскольку он позволяет избежать копирования данных в / из пользовательского пространства.

0
ответ дан 17 December 2019 в 02:21
поделиться
Другие вопросы по тегам:

Похожие вопросы: