Программирование сокетов C, отправляющее файл изображения «изображение не может быть отображено, поскольку оно содержит ошибку»

Я работаю над проектом, в котором реализую HTTP-сервер на C, и столкнулся с проблемой рендеринга изображений в веб-браузере. Каждый раз, когда я пытаюсь получить доступ к изображению через сервер, я получаю сообщение об ошибке «невозможно отобразить изображение, поскольку оно содержит ошибку».

Я тщательно проверил свой код, но не могу точно определить проблему. Может ли кто-нибудь подсказать мне, что я делаю неправильно? Ниже приведена упрощенная версия моего кода для обслуживания изображений:

main.c

#include <arpa/inet.h>
#include <errno.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>

#define PORT "8080"
#define BUFFSIZE 2048
#define RESSIZE 4096
#include "filehandle.h"
#include "httpresponse.h"

char *get_file_path(const char *request) {
    char *path = NULL;
    char *start = strstr(request, "GET ");
    if (start != NULL) {    start += 4; // Move past "GET "     char *end = strstr(start, " ");     if (end != NULL) {
        int path_length = end - start;
        path = (char *)malloc(path_length + 1);
        strncpy(path, start, path_length);
        path[path_length] = '\0';   }
    }
    return path; };

char *requestpath(int sockfd) {
    char *buffer = (char *)malloc(BUFFSIZE);

    int bytes = read(sockfd, buffer, BUFFSIZE);
    return buffer; };

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

    int status;
    struct addrinfo hints, *res;
    int sockfd;
    int bindstatus;

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;

    status = getaddrinfo(NULL, PORT, &hints, &res);
    if (status == -1) {     fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(status));
    }

    sockfd = socket(res->ai_family, res->ai_socktype, 0);
    if (sockfd == -1) {     fprintf(stderr, "socket error %s\n", strerror(sockfd));
    }

    int opt = 1;
    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt));

    bindstatus = bind(sockfd, res->ai_addr, res->ai_addrlen);
    if (bindstatus == -1) {     fprintf(stderr, "bind error %s\n", strerror(bindstatus));
    }

    freeaddrinfo(res);

    int l;
    l = listen(sockfd, 10);
    if (l != 0) {   fprintf(stderr, "LISTEN ERROR %s\n", strerror(l));
    }

    while (1) {     struct sockaddr_in client_addr;     int newfd;  socklen_t theiraddr = sizeof client_addr;   newfd = accept(sockfd, (struct sockaddr *)&client_addr, &theiraddr);

    if (newfd == -1) {
        fprintf(stderr, "accept error %s\n", strerror(errno));  } else {
        char str[INET_ADDRSTRLEN];
        inet_ntop(client_addr.sin_family, (struct inaddr *)&client_addr.sin_addr,
            str, INET_ADDRSTRLEN);
        printf("connection from %s\n", str);    }

    char *request = requestpath(newfd);     char *filepath = get_file_path(request);    printf("%s\n", filepath);   free(request);

    char *response = NULL;

    if (strcmp(filepath, "/") == 0 || strcmp(filepath, "/index.html") == 0) {
        response = content("index.html");

        char *http_response = (char *) malloc(RESSIZE);
        char *date = get_date_for_server();
        int content_length = strlen(response);
        int len,bytes_sent;
        int n = snprintf(http_response,RESSIZE,"HTTP/1.1 200 OK\r\nAcess-Control-Allow-Origin: *\r\nConnection: Keep-Alive\r\nDate: %s\r\nContent-Type: %s\r\nContent-Length: %llu\r\n\n%s",date,"text/html",content_length,response);
        len = strlen(http_response);
        bytes_sent = send(newfd,http_response,len,0);
        free(date);
        free(http_response);

        shutdown(newfd, SHUT_WR);
    }
    else if (strcmp(filepath,"/image.jpg") == 0){
        char http_header[256];
        char *date = get_date_for_server();

        struct file_data *fdata = NULL;
        fdata = read_file("image.jpg");
        if (fdata == NULL)
            fprintf(stderr,"Error opening binary file");
   
        uint8_t buff[fdata->length];
        FILE *wbf = fopen("test.jpg","wb");
        fwrite(buff,fdata->length,1,wbf);
        fclose(wbf);
        snprintf(http_header,256,"HTTP/1.1 200 OK\r\nContent-Type: image/jpeg\r\nContent-Length: %llu\r\n",fdata->length);
        send(newfd,http_header,strlen(http_header),0);
        send(newfd,fdata->data,fdata->length,0);
        printf("length is %ld\n",fdata->length);
        shutdown(newfd, SHUT_WR);
        fdata = NULL;

    }
    else
        shutdown(newfd, SHUT_WR);
    }
    close(sockfd);
    return 0; }

вот мой дескриптор файла.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

#define BUFFSIZE 2048

typedef struct file_data {
    uint8_t *data;
    size_t length;
}file_data;


struct file_data *read_file(const char *path){

        FILE *handler = NULL;
        struct file_data *fdata = NULL;
        size_t read_length = 0;
        uint8_t buffer[BUFFSIZE] = {0x00};

        fdata = malloc(sizeof(struct file_data));
        if (fdata == NULL)
            return NULL;

        bzero(fdata, sizeof(struct file_data));

        fdata->data = malloc(1);
        if (fdata->data == NULL){
            free(fdata);
            return NULL;
        }

        handler = fopen(path,"rb");
        if (handler == NULL){
            free(fdata->data);
            free(fdata);
            return NULL;
        }

        while(!feof(handler)){
            read_length = fread(buffer,1,BUFFSIZE,handler);
            fdata->data = realloc(fdata->data,fdata->length + read_length);
            if (fdata->data == NULL){
                fclose(handler);
                free(fdata);
                return NULL;
            }
            memcpy(fdata->data +fdata->length, buffer, read_length);
            fdata->length += read_length;
        }

        fclose(handler);

        return fdata;
}


// open the requested file and return the file pointer
FILE* requestfile(char *filename){
    FILE *fp;
    fp = fopen(filename,"r");
    if (fp != NULL){
        return fp;
    }
    else{
        perror("Error opening file\n");
    }
}

// count the byte size of file
int count_file_bytes(FILE *fp)
{
    int c,count = 0;
    if (fp != NULL){
        while((c = fgetc(fp)) != EOF)
            count++;
    }
    fclose(fp);
    return count;
}

// read the file content to buffer and return
char* content(char *filename)
{
    FILE *fp = requestfile(filename);
    int bytecount = count_file_bytes(fp);
    char *content = (char *) malloc(bytecount+1);

    FILE *file = requestfile(filename);
    size_t i;

    for(i=0;i<(bytecount+1);++i)
    {
        int c = fgetc(file);
        if ( c == EOF)
        {
            break;
        }

        content[i]= c;
    }

    content[i] = '\0';
    fclose(file);
    return content;



}

// get file extension before last '.'
char *get_file_ext(char *filename, int c)
{
    char *type = strrchr(filename,c);
    return type;
}


// read the file in binary format
char *read_binary_file(char *filename)
{
    FILE *fp = fopen(filename,"rb");
    if (fp == NULL)
        fprintf(stderr,"\t Error Opening file: %s\n",filename);
    long image_size;
    int c;
    int count = 0;
    fseek(fp,0,SEEK_END);
    image_size = ftell(fp);
    rewind(fp);
    char *buffer = (char *)malloc(image_size);

    fread(buffer,1,image_size,fp);

    fclose(fp);
    return buffer;
}

1) Почему « while( !feof(file))» всегда неверно?2) TCP — потоковый протокол, где данные — это просто поток байтов без начала и конца. Второй момент означает, что вам нужно использовать циклы как для записи, так и для чтения данных в сокеты или из них. И вам нужен какой-то способ узнать, когда и где заканчивается сообщение (например, отправка размера или использование какой-либо последовательности границ сообщения).

Some programmer dude 02.06.2024 14:17

В вашем коде также много утечек памяти.

Some programmer dude 02.06.2024 14:19

Примечание: вам следует добавить дополнительный \r\n между заголовками и содержимым. Developer.mozilla.org/en-US/docs/Web/HTTP/Messages

001 02.06.2024 14:47

Спасибо, что добавили дополнительный \r\n, исправивший проблему.

devMe 02.06.2024 16:57
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
4
57
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Проблема заключалась в отсутствии \r\n в заголовке и контенте. это решило проблему

snprintf(http_header,256,"HTTP/1.1 200 OK\r\nContent-Type: image/jpeg\r\nContent-Length: %llu\r\n\r\n",fdata->length);

В проблеме не хватало \r\n.

Другие вопросы по теме