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:
send(new_socket, http_header, sizeof(http_header), 0);
should bestrlen(http_header)
instead ofsizeof
.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.fread()
returns a size_t not a long (they may be the same type of course).You have use bytes_read so a short read results in a short write.
(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.As you run that send in a
while(1)
you probably want to move therewind(file)
into thatwhile
-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);