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; }
问题原因与解决方案
一、接收不到图片请求的核心原因
未处理HTTP持久连接且未关闭客户端连接
HTTP/1.1默认启用Keep-Alive,浏览器会复用同一个TCP连接发送多个请求(先请求index.html,再请求cat.jpg)。但当前代码在处理完第一个请求(/)后,既没有继续监听该连接的后续请求,也没有关闭连接。服务器直接回到accept等待新连接,而浏览器仍在等待当前连接的后续交互,导致图片请求无法被服务器接收。缺少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
相关产品推荐
相关产品推荐

