c++ 无法在hobby HTTP服务器中提供png文件和其他二进制文件

2jcobegt  于 2023-01-18  发布在  其他
关注(0)|答案(1)|浏览(112)

我正在用C编写一个HTTP服务器,服务静态文件基本上是可以的,但是当阅读.PNG文件或其他二进制文件时,我尝试过的每一种方法都失败了。我的主要问题是当我打开开发工具时,读取一个示例图像将会给予29. 56 KB的传输大小,对于我当前的方法,大小为29. 50 KB。给出的大小也与du-sh给出的32 KB不匹配。
我的第一个方法是将文件的内容压入一个字符串,然后调用一个函数来处理它。然而,如果内存正确的话,这也需要大约6 kb的空间。
我当前的方法是使用std::ifstream以二进制模式读取文件,我使用C
17的文件系统头和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,但是同样的问题仍然发生在我身上

jk9hmnmh

jk9hmnmh1#

if (send(this->newFd, contents.c_str(), strlen(contents.c_str()), 0) == -1) {

有两个窃听器,一个的价格,在这里。
这是用来发送二进制文件的内容。一次发送一个字节。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()做的正是它的名字所宣称的。
另一个错误:

char* buffer = new char[length];

有可能从后续代码中抛出异常。由于没有调用delete,因此内存泄漏。
C++Maven知道一个奇怪的窍门:他们很少使用new,而是使用容器,如std::vector,因此他们不必担心内存泄漏。

相关问题