You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言Web服务器无法向浏览器展示PNG文件的问题求助

C语言Web服务器PNG文件无法展示问题排查

问题现象

用C语言实现的Web服务器可正常展示txt和html文件,但PNG文件无法正常加载。访问http://localhost:10000/时,浏览器显示黑屏或中心小方块,同时触发ERR_CONTENT_LENGTH_MISMATCH错误。返回的HTTP响应头如下:

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

问题代码

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

问题原因及修复方案

1. 二进制文件打开方式错误

PNG是二进制文件,用fopen(file_name, "r")文本模式打开会触发换行符等转义操作,破坏二进制数据,同时导致ftell获取的文件大小不准确。
修复:改用二进制模式打开:

file = fopen(file_name, "rb");

2. HTTP头发送长度错误

当前用sizeof(http_header)(固定2048字节)发送HTTP头,会把缓冲区里的空字节也发给客户端,导致响应体起始位置偏移,浏览器解析失败。
修复:用strlen(http_header)获取实际头长度发送:

send(new_socket, http_header, strlen(http_header), 0);

3. 文件内容发送长度错误

循环中用sizeof(dbuffer)(4096字节)发送文件内容,但fread返回的bytes_read才是实际读取的字节数(最后一次读取可能不足4096),这会发送多余空字节,造成Content-Length与实际发送字节数不匹配。
修复:发送时使用bytes_read作为长度:

if (send(new_socket, dbuffer, bytes_read, 0) < 0) {
    perror("Error\n");
    return 1;
}

4. 文件指针未重置

第一次请求后文件指针已指向末尾,后续请求无法读取内容。需在每次处理请求前重置指针,或重新打开文件(更安全,避免并发问题)。
修复(重新打开文件示例):

// 在处理每个客户端请求时重新打开文件
FILE* file = fopen(file_name, "rb");
if (!file) {
    char* not_found = "HTTP/1.1 404 Not Found\r\nConnection: close\r\n\r\n404 Not Found";
    send(new_socket, not_found, strlen(not_found), 0);
    close(new_socket);
    continue;
}
// 重新计算文件大小
fseek(file, 0, SEEK_END);
long file_size = ftell(file);
rewind(file);
// 重新生成HTTP头
char http_header[2048] = {0};
sprintf(http_header, "HTTP/1.1 200 OK\r\nContent-Length: %ld\r\nConnection: close\r\nContent-Type: image/png\r\n\r\n", file_size);
// 发送头和文件内容...
// 发送完成后关闭文件
fclose(file);

5. 文件大小类型溢出风险

int file_size无法容纳超过2GB的大文件,建议改用long类型:

long file_size;

同时sprintf格式符改为%ld:

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

内容的提问来源于stack exchange,提问作者Billy Rogers

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 20:30:44