2013-04-26 84 views
1

我想知道,是否有可能找出响应中的哪一个流标头结束?http服务器响应(套接字)的标头和内容之间的区别

问题的背景是如下,我使用在C插座获取某个网站的内容,内容是采用gzip编码。我想直接从流中读取内容并使用zlib对gzip内容进行编码。但是,我如何知道gzip内容已启动并且http头已完成。

我粗略地尝试了两种方法,在我看来,这给了我一些奇怪的结果。首先,我读了整个流,并在终端中打印出来,我的http头像我预期的那样以“\ r \ n \ r \ n”结尾,但是在secound时间,我只是检索一次响应来获取头然后使用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,所以我可能只是监督一些简单的东西。

+1

头总是以空行结束。它由RFC定义。内容可以使用内容长度编码,也可以使用分块编码进行编码,这在处理上有很大不同。 – 2013-04-26 18:57:49

+0

你能解释一下为什么第二个工作?我的意思是我可以更改缓冲区大小,但仍然只能获得标题。服务器有什么特殊行为吗? – Iwan1993 2013-04-26 19:06:45

+1

看来,你的第二个例子可能会在偶然的事故和运气的情况下工作。您只需从插槽中连续读取两次。意外的时机可能是,第一个'recv()'得到头,第二个得到身体。但你不能指望这一点。您必须解析您收到的数据并查找空白行。 – Celada 2013-04-26 19:15:16

回答

6

您在循环中调用recv(),直到套接字断开连接或发生故障(并将接收到的数据以错误方式写入流中),将所有原始数据存储到您的char*缓冲区中。这不是读取HTTP响应的正确方法,特别是在使用HTTP保持活动的情况下(在这种情况下,在响应结束时不会发生断开连接)。您必须遵循RFC 2616中概述的规则。即:

  1. 直到遇到"\r\n\r\n"序列。不要再读取更多的字节。

  2. 分析接收到的报头,每规则RFC 2616 Section 4.4。他们会告诉您剩余响应数据的实际格式。

  3. 阅读每在2号发现的格式的数据。

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

  5. 根据需要处理收到的数据。

总之,你需要做更多的东西像这样代替(伪代码):

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"] == "multipart/byteranges") 
    { 
     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; 
相关问题