Отправка изображения через сокеты, не получающие штраф

#c #sockets

#c #сокеты

Вопрос:

Я пытаюсь отправить изображение в формате jpg из клиентского процесса на сервер через sockets . Изображение содержит двоичные данные, поэтому я хочу сделать это на основе низкоуровневого программирования, используя reads и writes . Я также отправляю данные изображения итерациями по 100 байт.

Это код, который я сделал, который не отправляет изображение идентично тому, что я хочу:

КЛИЕНТ

 void send_image(char *path, char *filename, int socket) {

    int fd = open(path, O_RDONLY); //I open the file of the image.jpg

    int n = 1;

    while (n > 0) {
        char img_data[100];
        n = read(fd, img_data, 100); //sending 100 bytes of image each iteration till n=0 (end of file)

        if (!n) break;

        int sending = 1;
        write(socket, amp;sending, sizeof(int)); //Tell the client the image still has data to send

        write(socket, img_data, strlen(img_data));

        usleep(250);
    }

    sending = 0; //Tell the server the image has been fully sent
    write(socket, amp;sending, sizeof(int));

    close(fd);
}
 

СЕРВЕР

 void receiving_image(char *path) {

    int receiving = 0;
    int j=0;
    char *image_data = NULL; //Variable to store all the image data

    read(socket, amp;receiving, sizeof(int)); //Reads that the client is going to send an image

    while (receiving) {

        char data[100]; //Variable that stores partial data (100 bytes) of an image on each iteration 
        read(socket, data, 100);

        image_data = realloc(image_data, (j   strlen(data)) * sizeof(char)); //Readjust the size of the main image data.
      
        for (int i=0; i<(int) strlen(data); i  ) {
           
            image_data[j] = data[i]; //copy the partial data of the image to the main variable of the image
            j  ;
        }

        j = (int) strlen(image_data); 

        read(socket, amp;receiving, sizeof(int)); //Read if the image is still sending
    }

    image_to_directory(path, image_data); //Copy image to directory
}
 

Это компилируется и работает нормально, но когда я проверяю каталог на стороне сервера, где было сохранено изображение, я вижу, что это не то же самое изображение, которое отправил клиент (я подтвердил через md5sum, и хэши не равны).

Есть ли что-то, чего мне не хватает?

Комментарии:

1. strlen(img_data) и strlen(data) ошибочны. Строковые функции могут использоваться только для строк, а не для двоичных данных. Используйте возвращаемое значение read вместо, чтобы получить количество прочитанных / полученных байтов.

2. read() возвращает -1 значение в случае ошибки, а не 0. Вы должны проверить <=0 .

3. Это не ваша проблема, поскольку я предполагаю, что сервер и клиент работают на одинаковых машинах, но int могут иметь разный размер на сервере и клиенте. Лучше использовать uint32_t или что-то в этом роде.

4. @12431234123412341234123 на какую строку вы ссылаетесь? Не while (n > 0) выполняет этого?

5. while (n > 0) слишком поздно. Если read возвращает -1 , код продолжается и использует результат read , прежде чем он получит шанс выйти из цикла. Фактическая неправильная проверка, на которую ссылаются if (!n)

Ответ №1:

Вы не должны использовать strlen для своих вычислений длины двоичных данных. Он предназначен только для завершенных строк (отсюда и название). У вас также есть крайне опрометчивые открытые вызовы для чтения / записи, что является рецептом катастрофы при отправке данных через сокеты.

Кажется, вы никогда не отправляете более 100 байт за раз, что в данном случае полезно для разработки более надежного протокола. Рассмотрим это:

  1. Первый октет — это количество uint8_t байтов N, и он будет в 0 .. 100.
  2. После подсчета байтов передается N байтов.
  3. Повторяйте 1-2, пока не останется больше байтов.
  4. Уведомить сервер о EOF, отправив один нулевой октет

Пример этого кода отправителя показан здесь.

 void send_image(const char *path, int socket)
{
    int fd = open(path, O_RDONLY); //I open the file of the image.jpg
    if (fd == -1)
        return;

    ssize_t n = 0;
    do
    {
        // note the first octet will prefix the length
        uint8_t img_data[101];
        n = read(fd, img_data 1, 100);
        if (n > 0)
        {
            // you never know  just how many bytes are going to
            // be sent, so setup the frame, but then ensure even
            // piecewise deliver can succeed.
            img_data[0] = (uint8_t)n;
            ssize_t sent = 0;
            size_t pos = 0;
            do
            {
                sent = write(socket, img_data pos, (n 1)-pos);
                if (sent < 0)
                    break;
                pos  = sent;
            } while ( pos < (n 1) amp;amp; sent > 0);
        }

    } while (n > 0);

    uint8_t done = 0;
    write(socket, amp;done, sizeof done); // not much we can do if this fails

    close(fd);
}
 

Я не утверждаю, что приведенный выше код будет даже компилироваться, но концепция должна быть достаточно очевидной. Однако это все. Очевидно, что можно / нужно сделать больше (контрольные суммы, параметры перезапуска и т. Д.), Но это основная предпосылка.

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