Linux Socket API实现HTTP服务器:图片文件传输损坏问题
HTTP服务器PNG/JPEG传输损坏,PDF正常的问题修复
我用Linux Socket API实现了带文件传输功能的HTTP服务器,目前遇到一个问题:传输PNG/JPEG图片时,写入本地的文件损坏无法打开,但PDF文件传输完全正常。
请求处理流程与代码
1. 请求接收函数
template <typename T> void server::printReceivedData(class acceptedSocket<T> *socket) { T buffer[1025]; int acceptedSocketFD = socket->getAcceptedSocketFileDescriptor(); while (true) { ssize_t bytesReceived = recv(acceptedSocketFD, buffer, sizeof(buffer), 0); if (bytesReceived <= 0) { std::cerr << "Receive failed! " << socket->getError() << "\n"; std::string file = __user->getFileInQueue(); if (!file.empty()) { addToFileTable(file.c_str(), 0); __user->clearFileInQueue(); if (send(acceptedSocketFD, "HTTP/1.1 302 Found\r\nLocation: /index.html\r\nConnection: close\r\n\r\n", 65, 0) == -1) std::cerr << "Failed to send response.\n"; } break; } std::cout << "\n____________________________________________________________\n\n"; buffer[bytesReceived] = '\0'; std::cout << buffer; HTTPrequestsHandler<T>(buffer, acceptedSocketFD, bytesReceived); } close(socket->getAcceptedSocketFileDescriptor()); delete socket; }
2. 文件写入路由函数
int user::addFilesRoute(const char *buffer, int acceptedSocketFileDescriptor, ssize_t __bytesReceived) { if (findString(buffer, "filename=")) { fileName.clear(); path.clear(); path = "interface/storage/"; const std::string t_buffer = std::string(buffer); std::regex fileNameRegex(R"(filename=\"([^\"]+)\")"); std::smatch match; if (std::regex_search(t_buffer, match, fileNameRegex)) fileName = match.str(1); path = path + fileName; addFileInQueue(fileName); } std::ofstream file(path, std::ios::out | std::ios::app | std::ios::binary); if (file.is_open()) { file.write(buffer, __bytesReceived); file.close(); } return EXIT_SUCCESS; }
问题现状
所有文件都能成功写入本地,但PNG/JPEG图片文件损坏无法打开,PDF文件正常。
补充说明
addFileInQueue:用于准备文件名并存入数据库的函数filename:保存当前HTTP请求文件名的全局变量path:指定文件保存路径的全局变量- 每次接收的buffer仅为1025字节的请求数据块,非完整请求
- 尝试过移除请求头部,但直接导致功能失效
问题根源与修复方案
1. 核心问题1:HTTP头部被写入文件
HTTP文件上传使用multipart/form-data格式,请求的第一个数据块包含HTTP头部(如Content-Disposition、Content-Type)和部分文件数据,后续块是纯文件数据。当前代码把每个接收的buffer完整写入文件,导致头部信息被混入图片文件中——图片格式对内容完整性要求极高,头部冗余数据直接导致文件损坏;而PDF格式对部分冗余数据容忍度更高,所以仍能打开。
2. 核心问题2:二进制数据被篡改
代码中执行了buffer[bytesReceived] = '\0',这个操作是为了打印字符串,但会破坏二进制数据:
- 当
bytesReceived等于sizeof(buffer)时,会覆盖buffer最后一个有效字节 - 即使未覆盖,这个操作也属于不必要的修改,可能干扰后续数据处理
3. 具体修复步骤
步骤1:移除buffer[bytesReceived] = '\0'
如果需要打印日志,单独处理字符串类型的请求头,不要修改原始接收的二进制buffer。
步骤2:准确分离HTTP头部与文件数据
需要解析multipart/form-data的边界(boundary),具体逻辑:
- 从请求的
Content-Type头中提取boundary值(例如从Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryxxx中获取----WebKitFormBoundaryxxx) - 当检测到
filename=后,继续读取数据直到遇到\r\n\r\n,这之后的内容才是真正的文件数据 - 后续接收的buffer中,要排除末尾的boundary结束标记(如
--boundary--) - 仅将分离出的文件数据部分写入目标文件,而非整个buffer
步骤3:修复全局变量的线程安全问题
fileName和path是全局变量,多客户端同时上传时会被覆盖,导致文件写入错误。建议改用类成员变量或请求上下文对象传递这些数据。
步骤4:优化文件写入逻辑
不要每次接收数据都打开/关闭文件,应该在开始写入时打开文件句柄,保持到整个文件传输完成后再关闭,减少IO开销和错误概率。
内容的提问来源于stack exchange,提问作者Sorin Tudose
相关产品推荐
相关产品推荐

