我必须在C中编写(作为一项任务)一个小型HTTP / 1.0服务器。
以下是我的问题:我不知道如何处理客户请求的网页规模非常大的情况。
我认为最好先读取整个文件,然后开始向客户端发送回复(包括状态行和标题),主要原因是我可以正确设置状态代码。例如,假设服务器已经读取并存储为客户端所需文件的一半,read()
失败。然后我会继续设置" HTTP / 1.0 500内部服务器错误"作为状态行。
这种方法的问题在于,如果文件很大,它需要太多内存(并且由于每个连接都由一个单独的线程处理,如果多个线程将相当大的文件存储为字符串,则内存使用量会增长更糟糕的是。)
作为一个解决方案,我考虑打开文件,发送状态行和标题,然后读入一个给定数量(不是太大)字节的缓冲区,并迭代地发送缓冲区中的内容,直到我发现读/发整个文件。
这解决了这个问题,但同样,如果read()
在文件中途发生故障,该怎么办?由于内部错误,无法满足客户端请求,因此500状态代码是合适的,但我已经通过套接字发送了200 OK消息!
如何在HTTP服务器中正常处理此问题?
答案 0 :(得分:1)
作为一个解决方案,我考虑打开文件,发送状态行和标题,然后读入一个给定量(不是太大)字节的缓冲区,并迭代地发送缓冲区中的内容,直到我读取/发送了整个文件。
这正是你应该做的。事先查询文件大小,以便将其放在Content-Length
响应头中,然后在发送了那么多字节时停止读取+发送循环。
如果您可以切换到HTTP 1.1,则还有其他选项。您可以省略Content-Length
标头,而是发送Transfer-Encoding: chunked
标头,然后您可以以chunked
格式发送每个缓冲区(请参阅RFC 2616 Section 3.6.1),其中每个块指定自己的字节大小。通过发送0长度的块来终止数据传输。这使您可以在不预先知道总大小的情况下发送/传输大量数据。但是此选项在HTTP 1.0中不可用。
这解决了这个问题,但是,如果read()在我文件的中途失败了怎么办?
您唯一能做的就是关闭套接字以表示传输已终止。如果发送Content-Length
标头(或者在HTTP 1.1分块的情况下,发送一个0长度的块),客户端将知道它何时收到了正确的文件结束,并且过早关闭是错误的。但是如果没有这些信息,套接字关闭是表示传输结束的唯一方式,并且客户端无法知道它是成功还是错误(HTTP 1.1确实能够恢复损坏的下载,但是HTTP 1.0没有。)
由于内部错误导致无法满足客户端请求,因此500状态代码是合适的,但我已经通过套接字发送了200 OK消息!
发送后,您无法更改响应状态。但是,如果你让客户知道如何检测文件的正确结尾,它就会知道如何检测到破坏的下载。