关于python:用-Python-实现一个简易版-HTTP-客户端

8次阅读

共计 4118 个字符,预计需要花费 11 分钟才能阅读完成。

此文为《用 Python 撸一个 Web 服务器》系列教程的一个补充,这个系列教程介绍了如何应用 Python 内置的 socket 库实现一个简易版的 Web 服务器。

之所以写这篇文章,是因为我发现很多人并不分明 HTTP 客户端的概念,认为只有浏览器才叫 HTTP 客户端。事实上并非如此,咱们在 Web 开发中常见的 Postman 爬虫程序 curl 命令行工具 等,这些都能够称为 HTTP 客户端。

服务器程序示例

这里以一个 Hello World 程序来作为示例服务器,实现如下:

# server.py

import socket
import threading


def process_connection(client):
    """解决客户端连贯"""
    # 接管客户端发来的数据
    data = b''
    while True:
        chunk = client.recv(1024)
        data += chunk
        if len(chunk) < 1024:
            break

    # 打印从客户端接管的数据
    print(f'data: {data}')
    # 给客户端发送响应数据
    client.sendall(b'HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n<h1>Hello World</h1>')

    # 敞开客户端连贯对象
    client.close()


def main():
    # 创立 socket 对象
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 容许端口复用
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    # 绑定 IP 和端口
    sock.bind(('127.0.0.1', 8000))
    # 开始监听
    sock.listen(5)

    while True:
        # 期待客户端申请
        client, addr = sock.accept()
        print(f'client type: {type(client)}\naddr: {addr}')

        # 创立新的线程来解决客户端连贯
        t = threading.Thread(target=process_connection, args=(client,))
        t.start()


if __name__ == '__main__':
    main()

服务器端程序不做过多解释,如有不明确的中央能够参考 用 Python 撸一个 Web 服务器 - 第 2 章:Hello-World 一节。

极简客户端

晓得了如何用 socket 库实现服务器端程序,那么了解客户端程序的实现就非常容易了。客户端程序代码实现如下:

# client.py

import socket

# 创立 socket 对象
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 指定服务器 IP 和端口,进行连贯
sock.connect(('127.0.0.1', 8000))
# 向 URL "/" 发送 GET 申请
sock.send(b'GET / HTTP/1.1\r\nHost: 127.0.0.1:8000\r\n\r\n')

# 接管服务端响应数据
data = b''
while True:
    chunk = sock.recv(1024)
    data += chunk
    if len(chunk) < 1024:
        break
# 打印响应数据
print(data)

# 敞开连贯
sock.close()

相对来说客户端程序要简略一些,创立 socket 对象的代码与服务器端程序并无差别,客户端 socket 对象依据 IP 和端口来连贯指定的服务器,建设好连贯后就能够发送数据了,这里依据 HTTP 协定结构了一个针对 / URL 门路的 GET 申请,为了简略起见,申请头中仅携带了 HTTP 协定规定的必传字段 Host,申请发送胜利后便能够接管服务器端响应,最初别忘了敞开 socket 连贯。

仅用几行代码,咱们就实现了一个极简的 HTTP 客户端程序,接下来对其进行测试。

首先在终端中应用 Python 运行服务器端程序:python3 server.py。而后在另一个终端中应用 Python 运行客户端程序:python3 client.py

能够看到客户端打印后果如下:

b'HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n<h1>Hello World</h1>'

以上,咱们实现了一个极简的 HTTP 客户端。

参考 requests 实现客户端

用 Python 写过爬虫的同学,肯定据说或应用过 requests 库,以下是应用 requests 拜访 Hello World 服务端程序的示例代码:

# demo_requests.py

import requests

response = requests.request('GET', 'http://127.0.0.1:8000/')
print(response.status_code)  # 响应状态码
print('--------------------')
print(response.headers)  # 响应头
print('--------------------')
print(response.text)  # 响应体 

在终端中应用 python3 demo_requests.py 运行此程序,将打印如下后果:

200
--------------------
{'Content-Type': 'text/html'}
--------------------
<h1>Hello World</h1>

接下来批改咱们下面实现的极简 HTTP 客户端程序,使其可能反对 response.status_coderesponse.headersresponse.text 性能。

# client.py

import socket
from urllib.parse import urlparse


class HTTPClient(object):
    """HTTP 客户端"""

    def __init__(self):
        # 创立 socket 对象
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 初始化数据
        self.status_code = 200
        self.headers = {}
        self.text = ''

    def __del__(self):
        # 敞开连贯
        self.sock.close()

    def connect(self, ip, port):
        """建设连贯"""
        self.sock.connect((ip, port))

    def request(self, method, url):
        """申请"""
        # URL 解析
        parse_result = urlparse(url)
        ip = parse_result.hostname
        port = parse_result.port or 80
        host = parse_result.netloc
        path = parse_result.path
        # 建设连贯
        self.connect(ip, port)
        # 结构申请数据
        send_data = f'{method} {path} HTTP/1.1\r\nHost: {host}\r\n\r\n'.encode('utf-8')
        # 发送申请
        self.sock.send(send_data)
        # 接管服务端响应的数据
        data = self.recv_data()
        # 解析响应数据
        self.parse_data(data)

    def recv_data(self):
        """接收数据"""
        data = b''
        while True:
            chunk = self.sock.recv(1024)
            data += chunk
            if len(chunk) < 1024:
                break
        return data.decode('utf-8')

    def parse_data(self, data):
        """解析数据"""
        header, self.text = data.split('\r\n\r\n', 1)
        status_line, header = header.split('\r\n', 1)
        for item in header.split('\r\n'):
            k, v = item.split(':')
            self.headers[k] = v
        self.status_code = status_line.split(' ')[1]


if __name__ == '__main__':
    client = HTTPClient()
    client.request('GET', 'http://127.0.0.1:8000/')
    print(client.status_code)
    print('--------------------')
    print(client.headers)
    print('--------------------')
    print(client.text)

代码实现比较简单,我写了较为具体的正文,置信你可能看懂。其中应用了内置函数 urlparse,此函数可能依据 URL 格局规定将 URL 拆分成多个局部。

在终端中应用 python3 client.py 运行此程序,打印后果与应用 requests 的后果完全相同。

200
--------------------
{'Content-Type': 'text/html'}
--------------------
<h1>Hello World</h1>

仅用几十行代码,咱们就实现了一个简易版的 HTTP 客户端程序,并且还实现了相似 requests 库的性能。

接下来你能够尝试用它去拜访事实世界中实在的 URL,比方拜访 http://httpbin.org/get,看看打印后果如何。

P.S.

Web 开发实质是围绕着 HTTP 协定进行的,HTTP 协定是 Web 开发的基石。所以对于何为 HTTP 服务端、何为 HTTP 客户端的概念不够清晰的话,实际上都是对 HTTP 协定不够了解。

最初,给大家留一道作业题,实现 requests 库的 response.json() 办法。

分割我:

  • 微信:jianghushinian
  • 邮箱:jianghushinian007@outlook.com
  • 博客地址:https://jianghushinian.cn/
正文完
 0