现在我遇到了一个问题。在请求中,我必须逐行读取和处理,但我不知道应该:
- 每次读取一个字节,还是
- 每次读取N个字节的块,将它们放入缓冲区中,然后逐个处理字节,再读取新的字节块。
什么是最佳选择,为什么?
此外,是否有其他解决方案?比如从套接字中一次读取一行的函数等?
一次只处理一个字节会影响性能。考虑使用适当大小的循环缓冲区。
读取缓冲区中任意大小可用的数据块。大多数情况下,您会得到短读取。检查读取字节中的http命令结束标记。处理完整的命令,下一个字节成为缓冲区头部。如果缓冲区已满,请将其复制到备份缓冲区,使用第二个循环缓冲区,报告错误或采取其他适当措施。
#define CHUNK_SIZE 1024
nextHttpBytesRead = 0;
nextHttp = NULL;
while (1)
{
size_t httpBytesRead = nextHttpBytesRead;
size_t thisHttpSize;
char *http = nextHttp;
char *temp;
char *httpTerminator;
do
{
temp = realloc(httpBytesRead + CHUNK_SIZE);
if (NULL == temp)
...
http = temp;
httpBytesRead += read(httpSocket, http + httpBytesRead, CHUNK_SIZE);
httpTerminator = strstr(http, "\r\n\r\n");
}while (NULL == httpTerminator)
thisHttpSize = ((int)httpTerminator - (int)http + 4; // Include terminator
nextHttpBytesRead = httpBytesRead - thisHttpSize;
// Adding CHUNK_SIZE here means that the first realloc won't have to do any work
nextHttp = malloc(nextHttpBytesRead + CHUNK_SIZE);
memcpy(nextHttp, http + thisHttpSize, nextHttpSize);
http[thisHttpSize] = '\0';
processHttp(http);
}
我已经实现了一个HTTP服务器库,非常类似于torak在这里的伪代码,http://code.google.com/p/openpgm/source/browse/trunk/openpgm/pgm/http.c。这个实现的最大速度提升来自于使所有东西都异步化,所以没有任何阻塞。
Indy,例如,采用缓冲区方法。当代码要求Indy读取一行时,它首先检查当前缓冲区是否存在换行符。如果没有,网络将被分块读取并附加到缓冲区,直到出现换行符为止。一旦出现,仅将数据截取到换行符从缓冲区中删除并返回给应用程序,使得剩余数据留在缓冲区中供下一次读取操作使用。这样可以在提供高效的网络I/O(读取套接字中当前可用的所有内容,缓冲它,并确认发送方不需要等待太长时间-这种方式需要较少的网络级别读取)的同时,保持简单的应用程序级别API(ReadLine、ReadStream等)之间取得良好平衡。
从每次读取一个字节开始(尽管在HTTP中,需要注意行以cr / lf结尾),因为这很简单。如果这不足够,请执行更复杂的操作。
每次读取一个字节数组缓冲区。由于用户和内核模式之间的多个上下文切换(实际上取决于libc),逐个字符读取将非常慢。
如果您读取缓冲区,需要准备好缓冲区可能未完全填充(请注意长度返回),缓冲区不包含足够的字节以行结束或缓冲区包含多个行的情况。
在网络应用程序中,将您的行或固定大小块请求映射到该变量流的缓冲区是一种常见模式(通常实现错误,例如可能出现0字节长度的答案)。高级语言将隐藏此复杂性。