【问题标题】:How do I stop reading from a socket if the HTTP response doesn't have a Content-Length header?如果 HTTP 响应没有 Content-Length 标头,如何停止从套接字读取?
【发布时间】:2009-11-03 22:56:09
【问题描述】:

以下代码 sn-p 读取 HTTP 响应。假设响应不包含 Content-Length 标头且 Content-Type 不是 text/html,如何终止 while 循环?

(1.不用担心分块传输编码 2.没有IO::Socket)


    while(defined($line = <SOCK>))
    {
        $data .= $line;

        if($line =~ /Content-length:/isg)
        {
            ($cl) = $line =~ /Content-Length: ([0-9]+)/i;
        }

        $line =~ s/\n//g;
        $line =~ s/\r//g;
        if($line eq '' && $cl)
        {
            read(SOCK, $data, $cl, length($data));
            last;
        }
        if($line =~ /<\/html>/) { last; }
    }

【问题讨论】:

  • 您不必检查defined ness,它会自动为您检查。
  • 如果您在 while() 中分配给自己的变量,则定义的检查不是隐式的。只有当您拥有 readline 运算符时,它才是隐含的。
  • 基于cmets,我想你可能有不同的问题。你真的在乎是否有内容长度,还是只想知道什么时候拥有所有内容?例如,如果服务器设置为保持活动状态,则一旦完成一个请求,您就可以在同一连接上创建另一个请求。如果发生这种情况,我认为您有不同的问题。

标签: perl http sockets


【解决方案1】:

如果没有 Content-Length,并且服务器没有使用分块传输编码,则数据在服务器关闭套接字时结束(您将获得 EOF)。详情参见HTTP规范中的相关章节:http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.4

您的代码示例中的真正问题是:

  1. 无法处理标头仅以“内容长度”结尾的情况,如X-Not-Really-Content-Length:
  2. 如果$cl 为零或未定义(这可能就是您所看到的!),则永远不要进入读取。由于您说没有 Content-Length 标头时它会中断,所以会发生$cl 保持未定义并且您永远不会进入您的读取块。

由于这是 perl,您可能想看看 LWP,它处理了 HTTP 处理的许多棘手部分。

【讨论】:

  • 您是否获得了分块编码?如果您发出 http/1.0 请求,它将不会使用分块编码。
  • @Murali,查看我的编辑 - 你的代码中有一些大错误 :)
  • bdonlan, if($line eq '' 只有在读取了所有标题后,此条件才会为真。所以列表中的第一名不是错误。
  • #2 可能非常罕见,但我明白你的意思。 #3 也不是错误,因为可以有没有内容长度的响应。
【解决方案2】:

如果您不想再从套接字读取,就不要从套接字读取。如果要关闭套接字,只需将其关闭即可。如果您希望服务器在处理完您的请求后关闭连接,请确保在您的请求中发送“连接:关闭”标头。

如果您认为您可能会伤害服务器的感受,您可以只阅读剩下的所有内容,而不用做任何事情。但是,这让服务器有机会向您传输无穷无尽的数据,让您的套接字永远打开。

如果您确实需要在代码中自己处理它,您可能希望分别处理标头和消息正文。看到完整的标题后,您可以检查它是否包含您需要的字段。如果没有,则关闭袜子并返回:

 sub read_sock {
    HEADER: while( <SOCK> ) {
      last if /^[\r\n]+$/; # blank line terminating header

      ... parse header lines into %headers...
      }

    unless( exists $headers{'content-length'} ) {
       warn "No Content-Length! Stopping!\n";
       close SOCK;
       return;
       }

    MESSAGE_BODY: while( <SOCK> ) {
      ... read(), etc ...
      }

    return $message_body;
 }

不过,使用 LWP::UserAgent 可能有更好的方法。您可以定义自己的内容处理程序并在其中做任何您喜欢的事情。

【讨论】:

    【解决方案3】:

    如果没有 content-length 标头,那么您有三个选项:

    1. 数据中的某种标志会告诉您何时完成
    2. 超时
    3. 等待服务器关闭套接字

    没有一个是非常健壮的。

    【讨论】:

    • 浏览器不依赖#2。检测数据结束的规则在 HTTP 协议的相关 RFC 中有很好的定义,包括 #1 和 #3。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-04-06
    • 2013-02-13
    • 2011-06-18
    • 1970-01-01
    • 2021-07-16
    • 2016-11-05
    • 2012-10-12
    相关资源
    最近更新 更多