http服务器响应(套接字)的标头和内容之间的差异

Posted

技术标签:

【中文标题】http服务器响应(套接字)的标头和内容之间的差异【英文标题】:Differ between header and content of http server response (sockets) 【发布时间】:2013-04-21 00:31:50 【问题描述】:

我想知道,是否有可能找出响应流中标头结束的位置?

问题的背景如下,我在c中使用套接字从网站获取内容,内容以gzip编码。我想直接从流中读取内容并使用 zlib 对 gzip 内容进行编码。但是我怎么知道gzip内容开始了,http头已经完成了。

我粗略地尝试了两种方法,在我看来,它们给了我一些奇怪的结果。首先,我读入整个流,并在终端中打印出来,我的 http 标头以“\r\n\r\n”结尾,就像我预期的那样,但是第二次,我只检索一次响应以获取标头然后使用 while 循环读取内容,此处标题以没有“\r\n\r\n”结尾。

为什么?哪种方式才是阅读内容的正确方式?

我只是给你代码,这样你就可以看到我是如何从服务器获得响应的。

//first way (gives rnrn)
char *output, *output_header, *output_content, **output_result;
size_t size;
FILE *stream;
stream = open_memstream (&output, &size);
char BUF[BUFSIZ];
while(recv(socket_desc, BUF, (BUFSIZ - 1), 0) > 0)

    fprintf (stream, "%s", BUF);

fflush(stream);
fclose(stream);

output_result = str_split(output, "\r\n\r\n");
output_header = output_result[0];
output_content = output_result[1];

printf("Header:\n%s\n", output_header);
printf("Content:\n%s\n", output_content);

.

//second way (doesnt give rnrn)
char *content, *output_header;
size_t size;
FILE *stream;
stream = open_memstream (&content, &size);
char BUF[BUFSIZ];

if((recv(socket_desc, BUF, (BUFSIZ - 1), 0) > 0)

    output_header = BUF;


while(recv(socket_desc, BUF, (BUFSIZ - 1), 0) > 0)

    fprintf (stream, "%s", BUF); //i would just use this as input stream to zlib

fflush(stream);
fclose(stream);

printf("Header:\n%s\n", output_header);
printf("Content:\n%s\n", content);

两者都给出相同的结果,将它们打印到终端,但第二个应该打印出更多的中断,至少我期望,因为它们在拆分字符串时会丢失。

我是 c 新手,所以我可能会监督一些简单的事情。

【问题讨论】:

标题总是以空行结束。它由 RFC 定义。内容可以用内容长度编码,也可以用分块编码,这在处理上有很大的不同。 你能解释一下为什么第二个有效吗?我的意思是我可以更改缓冲区大小,但仍将仅获取标题。服务器有什么特殊行为吗? 看来您的第二个示例在某些时候可能完全是偶然和运气。您只是连续两次从套接字读取。偶然的时机可能是第一个recv() 获得标题,第二个获得正文。但你不能指望这一点。您必须解析收到的数据并查找空白行。 以分块形式,内容被分段传输。首先,您需要检查什么是“传输编码”标头值(是的,不区分大小写)。如果它被“分块”,你就有麻烦了。您将得到一个字符串,而不是简单的内容长度,它包含下一段的长度,然后是数据,然后是下一个长度,依此类推。如果长度为 0,这是最后一部分,在它之后可能会有额外的头...如果您需要详细信息,您需要检查 RFC2616。 【参考方案1】:

您在循环中调用recv(),直到套接字断开连接或失败(并将接收到的数据以错误的方式写入您的流),将所有原始数据存储到您的char* 缓冲区中。这不是读取 HTTP 响应的正确方法,尤其是在使用 HTTP keep-alives 的情况下(在这种情况下,响应结束时不会发生断开连接)。您必须遵守RFC 2616 中列出的规则。即:

    读取直到遇到"\r\n\r\n" 序列。这将终止响应标头。在此之前不要再读取任何字节。

    根据RFC 2616 Section 4.4 中的规则分析接收到的标头。它们会告诉您剩余响应数据的实际格式。

    根据 #2 中发现的格式读取剩余数据(如果有)。

    如果响应使用 HTTP 1.1,请检查接收到的标头是否存在 Connection: close 标头,或者如果响应使用 HTTP 0.9 或 1.0,则检查是否缺少 Connection: keep-alive 标头。如果检测到,请关闭您的套接字连接端,因为服务器正在关闭它的端部。否则,保持连接打开并将其重新用于后续请求(除非您使用完连接,在这种情况下关闭它)。

    根据需要处理接收到的数据。

简而言之,你需要做更多类似这样的事情(伪代码):

string headers[];
byte data[];

string statusLine = read a CRLF-delimited line;
int statusCode = extract from status line;
string responseVersion = extract from status line;

do

    string header = read a CRLF-delimited line;
    if (header == "") break;
    add header to headers list;

while (true);

if ( !((statusCode in [1xx, 204, 304]) || (request was "HEAD")) )

    if (headers["Transfer-Encoding"] ends with "chunked")
    
        do
        
            string chunk = read a CRLF delimited line;
            int chunkSize = extract from chunk line;
            if (chunkSize == 0) break;

            read exactly chunkSize number of bytes into data storage;

            read and discard until a CRLF has been read;
        
        while (true);

        do
        
            string header = read a CRLF-delimited line;
            if (header == "") break;
            add header to headers list;
        
        while (true);
    
    else if (headers["Content-Length"] is present)
    
        read exactly Content-Length number of bytes into data storage;
    
    else if (headers["Content-Type"] begins with "multipart/")
    
        string boundary = extract from Content-Type header;
        read into data storage until terminating boundary has been read;
    
    else
    
        read bytes into data storage until disconnected;
    


if (!disconnected)

    if (responseVersion == "HTTP/1.1")
    
        if (headers["Connection"] == "close")
            close connection;
    
    else
    
        if (headers["Connection"] != "keep-alive")
            close connection;
    


check statusCode for errors;
process data contents, per info in headers list;

【讨论】:

以上是关于http服务器响应(套接字)的标头和内容之间的差异的主要内容,如果未能解决你的问题,请参考以下文章

如何将 HTTP 标头/正文与 PHP 套接字请求隔离开来

HTTP响应标头中“允许”和“访问控制允许方法”之间的区别?

如何防止 HttpWebRequest 的数据包碎片

HTTP 缓存

在 Angular 8 的 http 响应中看不到任何响应标头

一篇文章搞懂docker版本和es7.7