使用Python实现带有套接字(没有http库)的HTTP客户端

时间:2017-12-05 16:44:55

标签: python http request

出于教育目的,并且没有任何重要性,我想实现一个脚本,该脚本可以生成简单的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-lengt" (让我们假设它永远在那里)。如何在不必sock.recv (1000000000000000000)的情况下阅读答案的所有内容?

1 个答案:

答案 0 :(得分:1)

通常,您会在循环中读取一定数量的字节(例如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())

请注意,您必须将“连接”标头设置为“关闭”。 (或使用HTTP 1.0)。否则,由于默认情况下持久连接,循环将挂起,如HTTP 1.1中实现的那样。

可替换地,您可以读取第一个字节并解析它们以获取HTTP标头。如果有Content-Length标头,您可以使用它来计算ramaining字节。

...
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,cookie,重定向等),因此最好使用专为HTTP请求设计的库。