【问题标题】:How do I gracefully close a socket with a persistent HTTP connection?如何优雅地关闭具有持久 HTTP 连接的套接字?
【发布时间】:2021-12-31 21:51:13
【问题描述】:

我正在用 Python 编写一个非常简单的客户端,它从 WWW 获取 HTML 页面。这是我到目前为止提出的代码:

import socket

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(("www.mywebsite.com", 80))
sock.send(b"GET / HTTP/1.1\r\nHost:www.mywebsite.com\r\n\r\n")

while True:
    chunk = sock.recv(1024) # (1)
    if len(chunk) == 0:
        break
    print(chunk)

sock.close()

问题是:作为一个默认的 HTTP/1.1 连接,代码卡在# (1) 等待传输结束后来自服务器的更多数据。

我知道我可以通过 a) 添加 Connection: close 请求标头或 b) 为套接字设置超时来解决此问题。此处的非阻塞套接字无济于事,因为select() 系统调用仍会挂起(除非我对其设置超时,但这只是另一种形式的情况 b)。

那么有没有另一种方法可以做到这一点,同时保持连接持久?

【问题讨论】:

  • 您根本没有解析响应以知道它何时真正结束。请参阅 RFC 2616 Section 4.4RFC 7230 Section 3.3.3。然后,您可以在响应结束和您想在同一连接上发送新请求之间做任何您想做的事情。此外,您需要检查 Connection: close 标头或 Keep-Alive 标头,指示连接超时和/或每个连接允许的最大请求数。
  • @RemyLebeau 如此解析响应标头,特别是 Content-Length 似乎是要走的路。
  • 解析不仅仅涉及标题。请阅读我提到的 RFC,不仅仅是处理 Content-Length。请参阅我的past answers 关于这个主题

标签: python http sockets networking network-programming


【解决方案1】:

正如在 cmets 中已经说过的,如果您要尝试编写一个全能歌唱、全能跳舞的 HTTP 处理器,则需要考虑很多事情。但是,如果您只是在练习使用套接字,那么请考虑一下。

假设您知道响应将如何结束。例如,如果我们基本上按照您在 Google 主页面的代码中所做的操作,我们知道响应将以“\r\n\r\n”结尾。所以,我们能做的就是一次读取 1 个字节,并注意那个终止序列。

此代码不会为您提供完整的 Google 主页,因为正如您所见,响应是分块的 - 这是一个全新的球类游戏。

说了这么多,你可能会觉得这很有启发性:

import socket

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
    sock.connect(('www.google.com', 80))
    sock.send(b'GET / HTTP/1.1\r\nHost:www.google.com\r\n\r\n')
    end = [b'\r', b'\n', b'\r', b'\n']
    d = []
    while d[-len(end):] != end:
        d.append(sock.recv(1))
    print(''.join(b.decode() for b in d))
finally:
    sock.close()

【讨论】:

    猜你喜欢
    • 2012-10-29
    • 2011-08-07
    • 2015-06-25
    • 2021-12-12
    • 2021-06-03
    • 2010-12-01
    • 1970-01-01
    • 2011-12-12
    • 2019-06-27
    相关资源
    最近更新 更多