Home > Net >  Problem displaying png file from web server to client web browser in C
Problem displaying png file from web server to client web browser in C

Time:01-29

I am implementing a web server in C and trying to display different types of files to a client browser such as Chrome. I got it to work for files such as txt and html, however I can't seem to get it to work for png files.

#include <unistd.h>
#include <stdio.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <string.h>
#include <stdlib.h>

#define PORT 10000

int main() {

    FILE* file;
    char* file_name = "img.png";
    int file_size;

    file = fopen(file_name, "r");
    if (file == NULL) {
        printf("Error opening file!\n");
        return 1;
    }

    fseek(file, 0, SEEK_END);
    file_size = ftell(file);
    rewind(file);

     // need http_header for proper HTTP response to client or there will be errors
    char http_header[2048] = {0};

    sprintf(http_header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nConnection: close\r\nContent-Type: image/png\r\n\r\n", file_size);
    printf("%s", http_header);    

    // buffer for client HTTP request
    char buffer[1024] = {0};

    int server_fd;
    
    // defining socket's port and IP address
    struct sockaddr_in address;
    int addrlen = sizeof(address);

    // htons host-to-net short (16-bit) translation

    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(PORT);

    // Creating socket file descriptor
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
        perror("socket failed");
        exit(EXIT_FAILURE);
    }

    // options for the socket file descriptor
    int opt = 1;

    if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) {
        perror("setsockopt");
        exit(EXIT_FAILURE);
    }

    // bind socket to the port and IP address

    if (bind(server_fd, (struct sockaddr *) &address,
             sizeof(address)) < 0) {
        perror("bind failed");
        exit(EXIT_FAILURE);
    }

    // server starts listening and can have up to 3 max clients queued for backlog

    if (listen(server_fd, 3) < 0) {
        perror("listen");
        exit(EXIT_FAILURE);
    }

    // client socket

    int new_socket;
    int valread;

    // while loop cause server will keep listening for requests
    while(1) {
        // address and addrlen are result arguments that will have the client's info
        // we redefined address and addrlen and we can probably use this later
        if ((new_socket = accept(server_fd, (struct sockaddr *) &address,
                                (socklen_t * ) & addrlen)) < 0) {
            perror("accept");
            exit(EXIT_FAILURE);
        } 

        // we read in the client's HTTP request just for console output purposes

        valread = read(new_socket, buffer, 1024);
        printf("%s\n", buffer);

        // we send client socket the HTTP response
        send(new_socket, http_header, sizeof(http_header), 0);
        //send(new_socket, ht, sizeof(response), 0);
        char dbuffer[4096];
        memset(dbuffer, 0, 4096);

        long bytes_read;
        if (file > 0) {
            while ((bytes_read = fread(dbuffer, 1, 4096, file)) > 0) {
                if (send(new_socket, dbuffer, sizeof(dbuffer), 0) < 0) {
                    perror("Error\n");
                    return 1;
                }
            }
        }
        
        
        close(new_socket);
    }
    fclose(file);

    //printf("Hello message sent\n");

    
    return 0;
}

When I connect to http://localhost:10000/, I just get a black screen. Sometimes, I get an occasional small white square in the center. When I am checking the HTTP response sent to my client, I see this:

HTTP/1.1 200 OK Content-Length: 177589 Connection: close Content-Type: image/png

Along with that, I see that I am getting a ERR_CONTENT_LENGTH_MISMATCH error.

CodePudding user response:

  1. send(new_socket, http_header, sizeof(http_header), 0); should be strlen(http_header) instead of sizeof.

  2. if (file > 0) { doesn't do anything, you already check if the fil exist after you open it, and as it's a pointer you only want to check if it's NULL or not.

  3. fread() returns a size_t not a long (they may be the same type of course).

  4. You have use bytes_read so a short read results in a short write.

  5. (not fixed) Browsers may request other files. Firefox, for instance, asks for a /favicon.ico so you want to reject that with a 403. With the above changes curl returns file as is.

  6. As you run that send in a while(1) you probably want to move the rewind(file) into that while-loop.

        size_t bytes_read;
        do {
            bytes_read = fread(dbuffer, 1, BUFFER_LEN, file);
            ssize_t bytes_sent;
            for(size_t n = 0; n < bytes_read; n  = bytes_sent) {
                bytes_sent = send(new_socket, dbuffer   n, bytes_read - n, 0);
                if(bytes_sent == -1) {
                    perror("send failed");
                    goto err;
                }
            }
        } while(bytes_read == BUFFER_LEN);
err:
        close(new_socket);
        rewind(file);
  • Related