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

C++ HTTP Socket未捕获浏览器图片请求问题及处理方案

问题描述

用C++实现简易Web服务器,已成功发送带超链接的text/html数据,但在HTML中添加<img src="cat.jpg">后,浏览器Network栏显示index.html状态200 OK,却未发送图片请求,页面持续加载无法显示图片。打印请求发现反复得到Request file: /,始终捕获不到GET /cat.jpg HTTP/1.1请求。

可复现代码

HTML文件(index.html)

<!Doctype html>
<html>
    <head>
        <title>My Simple Web Server</title>
    </head>
    <body>
        <h1>Hello, Web Server!</h1>
        <img src="cat.jpg">
    </body>
</html>

C++服务器代码

#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <strings.h>
#include <sstream>
#include <string>
#include <vector>
#include <fstream>
#include <cstring>

std::vector<std::string> 
parse_request(char request_string[], ssize_t r_len)
{
    std::vector<std::string> tokens;
    std::istringstream r_stream (request_string);
    std::string tok;
    int counter = 0;
    while (r_stream >> tok && counter++ < 3)
        tokens.push_back(tok);

    return tokens;
}

int main()
{
    sockaddr_in server_addr, client_addr;
    int socket_fd { socket(AF_INET, SOCK_STREAM, 0) }, newclientfd;
    bzero((char*) &server_addr, sizeof(server_addr));

    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port = htons(8000);    

    if (bind(socket_fd, (sockaddr*) &server_addr, sizeof(sockaddr_in)) < 0)
        perror("ERROR: bind");
    
    socklen_t clientlen {(socklen_t) sizeof(sockaddr_in)};

    if (listen(socket_fd, 5) < 0)
    {
        perror("ERROR: listen");
        exit(1);
    }

    while (true)
    {
        char recv_buffer[1024];
        newclientfd = accept(socket_fd, (sockaddr*) &client_addr, &clientlen);
        ssize_t recv_size { recv(newclientfd, recv_buffer, 1024, 0) };
        std::vector<std::string> request { parse_request(recv_buffer, strlen(recv_buffer)) };
        std::string fname { request[1] };

        std::cout << "Request file: " << fname << std::endl;

        /* For creating full file path. */
        std::ostringstream string_builder;
        
        /* Build our full path to the html file. */
        string_builder << ".";

        if (request[1] == "/")
            string_builder << "/index.html";
        else 
            string_builder << request[1];
        
        /* Open the file */
        std::ifstream html_file(string_builder.str());
        html_file.seekg(0, std::ios::end);
        int file_size = html_file.tellg();
        std::string file_buffer(file_size, ' ');
        html_file.seekg(0);
        html_file.read(&file_buffer[0], file_size);
        const char* buf = file_buffer.c_str();

        char page_buffer[4096];
        sprintf(page_buffer, "HTTP/1.1 200 OK\nContent-Length: %d\n\n%s", file_size, buf);
        printf("%s", page_buffer);

        /* Send it to the client. */
        if (send(newclientfd, (void*) page_buffer, strlen(page_buffer), 0) == -1)
            fprintf(stderr, "Sending to client failed! Error code %d\n", errno);
    }

    return 0;
}

问题原因与解决方案

一、接收不到图片请求的核心原因

  1. 未处理HTTP持久连接且未关闭客户端连接
    HTTP/1.1默认启用Keep-Alive,浏览器会复用同一个TCP连接发送多个请求(先请求index.html,再请求cat.jpg)。但当前代码在处理完第一个请求(/)后,既没有继续监听该连接的后续请求,也没有关闭连接。服务器直接回到accept等待新连接,而浏览器仍在等待当前连接的后续交互,导致图片请求无法被服务器接收。

  2. 缺少Content-Type响应头
    服务器返回的响应中没有指定Content-Type,浏览器无法准确识别返回内容的类型,可能影响后续资源的加载逻辑,比如无法确认返回的是HTML,进而不触发图片资源的请求。

二、处理HTML页面多文件的正确方式

1. 明确管理连接生命周期

  • 最简单的方式:在响应头中添加Connection: close,告诉浏览器本次连接在响应发送后关闭,浏览器会重新建立连接发送图片请求,发送响应后调用close(newclientfd)关闭连接。
  • 进阶方式:支持Keep-Alive,在客户端连接的循环中持续接收请求,直到连接关闭或超时。

2. 完善响应头,匹配文件类型

根据请求的文件类型添加对应的Content-Type:

  • HTML文件:Content-Type: text/html; charset=utf-8
  • JPG图片:Content-Type: image/jpeg
  • PNG图片:Content-Type: image/png

3. 增加文件存在性检查

当前代码未处理文件不存在的情况,会导致程序崩溃。需添加检查,返回404响应。

修正后的关键代码片段

1. 添加响应头并关闭连接

// 根据文件类型设置Content-Type
std::string content_type = "text/html; charset=utf-8";
if (fname.find(".jpg") != std::string::npos || fname.find(".jpeg") != std::string::npos) {
    content_type = "image/jpeg";
} else if (fname.find(".png") != std::string::npos) {
    content_type = "image/png";
}

char page_buffer[4096];
sprintf(page_buffer, "HTTP/1.1 200 OK\nContent-Length: %d\nContent-Type: %s\nConnection: close\n\n%s", 
        file_size, content_type.c_str(), buf);

send(newclientfd, (void*) page_buffer, strlen(page_buffer), 0);
close(newclientfd); // 关闭客户端连接

2. 文件存在性检查

std::ifstream html_file(string_builder.str());
if (!html_file.is_open()) {
    // 返回404响应
    const char* not_found = "HTTP/1.1 404 Not Found\nContent-Length: 13\nConnection: close\n\n404 Not Found";
    send(newclientfd, not_found, strlen(not_found), 0);
    close(newclientfd);
    continue;
}
// 后续文件读取逻辑...

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 09:57:41