我在C中创建一个简单的Web服务器,以便在教育目的中提供静态文件。我创建了一个向客户端发送文件的函数,它为整个文件分配内存块。如果服务器硬件具有4 GB内存,并且客户端请求大,例如,大小为5 GB的数据库文件,该怎么办?它会不会导致我的应用程序崩溃?
我的serve_file函数的主体(请注意,省略了错误处理):
long fsize;
FILE *fp = fopen(filename, "rb");
fseek(fp, 0, SEEK_END);
fsize = ftell(fp);
rewind(fp);
char *buf = (char*) malloc(fsize);
fread(buf, fsize, 1, fp);
Server_TCP_Send(socket, buf, fsize);
fclose(fp);
free(buf);
答案 0 :(得分:2)
最好的方法是使用循环以可管理的块读取和提供数据。这样,文件的大小无关紧要。您的方法可能会也可能不会导致系统崩溃。如果它没有崩溃,它可能会开始使用虚拟内存将数据分页到磁盘。这会导致大量的颠簸并使系统变慢,如果其他程序试图运行,这会特别糟糕。
答案 1 :(得分:2)
如果服务器硬件具有4 GB内存,并且客户端请求大,例如,大小为5 GB的数据库文件,该怎么办?它不会使我的申请崩溃吗?
也许。也许不吧。 C标准中没有任何内容直接涉及这个问题。即使在实践中,C实现支持的已分配对象的大小也不一定限于物理内存的大小。现代机器具有虚拟存储器系统,可以想象可以支持诸如您描述的分配。如果分配确实成功,那么没有特别的理由认为您的服务器会崩溃。
这里的主要风险是您不检查分配是否成功。在失败的合理事件中,malloc()
调用将返回空指针。将空指针作为fread()
的第一个参数传递将产生未定义的行为。这很可能表现为像这样的情况下的崩溃,但不需要这样做。如果您检查malloc()
电话的返回值,则可以优先失败而不是崩溃。
我注意到您也没有检查fread()
的返回值。如果失败,那么您最终可能会向客户端发送垃圾。作为一般规则,如果您确实关心是否发生错误,则应检查函数调用的返回值是否存在错误指示。
话虽如此,确实有更好的方法来做你想做的事。除了显着的失败风险之外,在发送文件之前将整个文件读入内存可能会在转移到客户端实际开始之前引入明显的延迟。很可能足以实现自动或有机超时。在这种意义上,从较小的块读取很可能会快得多。还有其他选择,例如将文件映射到内存而不是实际读取它。