使用Python实现基于套接字的HTTP客户端(无需使用http库)

3

出于教育目的,没有任何重要性,我想实现一个脚本,可以进行简单的HTTP请求,并在控制台中显示答案内容(以纯文本形式)。我用以下代码实现了:

import socket
import sys

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

server_address = ('localhost', 8080)
print >>sys.stderr, 'connecting to %s port %s' % server_address
sock.connect(server_address)

message = 'GET /php.php HTTP/1.1\r\n'
message += 'Host: localhost:8080\r\n\r\n'
print >>sys.stderr, 'sending "%s"' % message
sock.sendall(message)

data = sock.recv(10000000)
print >>sys.stderr, 'received "%s"' % data

sock.close()

我只需构建HTTP请求、将其发送到服务器并等待答案。

现在问题来了:我不知道如何读取整个答案,我知道有一个标题是“content-length”(假设它总是存在的)。如何在不必执行sock.recv(1000000000000000000)的情况下读取所有内容?

1个回答

5
通常情况下,您需要在循环中读取特定数量的字节(例如1024)。如果recv返回任何字节,则将其附加到数据中,否则请中断循环并关闭连接。
import socket

server_address = ('httpbin.org', 80)
message  = b'GET / HTTP/1.1\r\n'
message += b'Host: httpbin.org:80\r\n'
message += b'Connection: close\r\n'
message += b'\r\n'

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(server_address)
sock.sendall(message)

data = b''
while True:
    buf = sock.recv(1024)
    if not buf:
        break
    data += buf

sock.close()
print(data.decode())

请注意,您需要将连接头设置为“close”(或使用HTTP 1.0)。否则,默认情况下由HTTP 1.1实现的持久连接会导致循环挂起。
另外,您可以读取前几个字节并解析它们以获取HTTP标头。如果有Content-Length标头,则可以使用它来计算剩余的字节数。
...
data = b''
while b'\r\n\r\n' not in data:
    data += sock.recv(1)

header = data[:-4].decode()
headers = dict([i.split(': ') for i in header.splitlines()[1:]])
content_length = int(headers.get('Content-Length', 0))

if content_length:
    data += sock.recv(content_length)
...

通过在sendrecv中使用字节,这个示例也适用于Python3。但是,这只是一个非常基本的示例,在许多情况下会失败(如HTTPS、cookies、重定向等),因此最好使用专为HTTP请求设计的库。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接