我正在用C编写一个HTTP服务器,服务静态文件基本上是可以的,但是当阅读.PNG文件或其他二进制文件时,我尝试过的每一种方法都失败了。我的主要问题是当我打开开发工具时,读取一个示例图像将会给予29. 56 KB的传输大小,对于我当前的方法,大小为29. 50 KB。给出的大小也与du-sh
给出的32 KB不匹配。
我的第一个方法是将文件的内容压入一个字符串,然后调用一个函数来处理它。然而,如果内存正确的话,这也需要大约6 kb的空间。
我当前的方法是使用std::ifstream以二进制模式读取文件,我使用C17的文件系统头和std::filesystem::file_size
获取文件的大小,我将内容读入缓冲区,然后调用函数一次发送一个字节的缓冲区内容
void WebServer::sendContents(std::string contents) {
if (send(this->newFd, contents.c_str(), strlen(contents.c_str()), 0) == -1) {
throw std::runtime_error("Server accept: " + std::string(strerror(errno)));
}
}
void WebServer::sendFile(std::string path) {
path = "./" + path;
std::string fileCont; //File contents
std::string mimeType; //Mime type of the file
std::string contLength;
std::string::size_type idx = path.rfind('.');
if (idx != std::string::npos) mimeType = this->getMimeType(path.substr(idx + 1));
else mimeType = "text/html";
std::filesystem::path reqPath = std::filesystem::path("./" + path).make_preferred();
std::filesystem::path parentPath = std::filesystem::path("./");
std::filesystem::path actualPath = std::filesystem::canonical(parentPath / reqPath);
if (!this->isSubDir(actualPath, parentPath)) { this->sendRoute("404"); return; }
std::ifstream ifs;
ifs.open(actualPath, std::ios::binary);
if (ifs.is_open()) {
//Get the size of the static file being server
std::filesystem::path staticPath{path};
std::size_t length = std::filesystem::file_size(staticPath);
char* buffer = new char[length];
*buffer = { 0 }; //Initalize the buffer that will send the static file
ifs.read(buffer, sizeof(char) * length); //Read the buffer
std::string resp = "HTTP/1.0 200 OK\r\n"
"Server: webserver-c\r\n"
"Content-Length" + std::to_string(length) + "\r\n"
"Content-type: " + mimeType + "\r\n\r\n";
if (!ifs) std::cout << "Error! Only " << std::string(ifs.gcount()) << " could be read!" << std::endl;
this->sendContents(resp); //Send the headers
for (size_t i=0; i < length; i++) {
std::string byte = std::string(1, buffer[i]);
this->sendContents(byte);
}
delete buffer; //We do not need megs of memory stack up, that shit will grow quick
buffer = nullptr;
} else {
this->sendContents("HTTP/1.1 500 Error\r\nContent-Length: 0\r\nConnection: keep-alive\r\n\r\n"); return;
}
ifs.close();
}
应该注意,这个-〉newFd是一个套接字描述符
还应该注意的是,我已经尝试查看this question here,但是同样的问题仍然发生在我身上
1条答案
按热度按时间jk9hmnmh1#
有两个窃听器,一个的价格,在这里。
这是用来发送二进制文件的内容。一次发送一个字节。
sendContents
被使用,显然,一次发送一个字节,在这里。这是可怕的效率低下,但它不是bug。第一个bug如下。您的二进制文件中有大量字节为
00
。在这种情况下,
contents
将自豪地包含这个00
字节,这里。c_str()
返回一个指向它的指针。strlen()
然后得出结论,它正在接收一个空字符串作为输入,并做出一个宏伟的声明,该字符串包含0个字符。最后,
send
的第三个参数将为0。根本不会发送任何字节,而不是著名的
00
字节。第二个bug将在低效的算法得到修复后出现,并且
sendContents
习惯于一次发送多个字节。send()
包含一个秘密:除了-1
之外,这个系统调用还可以返回其他值来指示失败。2例如,发送的实际字节数。3因此,如果send()
被调用来发送,比如说,100个字节,它可能决定只发送30个字节,返回30个,剩下70个未发送的字节由你来承担。实际上,这已经是显示的代码中的一个bug了。
sendContents()
也被用来发送整个resp
字符串。这是,呃,在一个100字节的附近。给予或拿一打。你要依靠这座纸牌屋:
send()
总是在完成它的工作,在这个特定的例子中,没有松懈,实际上发送了整个HTTP/1.0
响应字符串。但是,
send()
是一个著名的懒鬼,无论如何,你不能保证这真的会发生,我有很好的权威,即将到来的13号星期五,你的send()
会突然决定偷懒。因此,要修复显示的代码:
1.实现适当的逻辑来处理
send()
的返回值。1.不要使用
c_str()
,后面跟strlen()
,因为:A)对于包含二进制数据的字符串来说,它是坏的,B)这个复杂的例程只是重新发明了一个叫做size()
的轮子,你会很高兴地知道size()
做的正是它的名字所宣称的。另一个错误:
有可能从后续代码中抛出异常。由于没有调用
delete
,因此内存泄漏。C++Maven知道一个奇怪的窍门:他们很少使用
new
,而是使用容器,如std::vector
,因此他们不必担心内存泄漏。