「Python网络编程」简述HTTP协议/模拟实现百度页面(五)

博主前言:

上次博客讲述了迭代器和生成器的相关知识点,在实现多任务的能力方面已经很不错了,接下来就是要勤加练习,掌握各种实现多任务的方式。

1. HTTP

HTTP(Hyper Text Transfer Protocol),全称超文本传输协议。
HTTP是因特网上应用最为广泛的一种网络传输协议,所有的WWW文件都必须遵守这个标准。

1.1 HTTP的特性

  1. HTTP是一个基于TCP/IP通信协议来传递数据(HTML文件, 图片文件等),默认端口:80
  2. HTTP是无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。
  3. HTTP是无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。
  4. HTTP的请求数据包括请求头(request header)和请求体(request body)在这里插入图片描述
  5. HTTP的响应数据包括响应头(response header)和响应体(response body)在这里插入图片描述

1.2 TCP的三次握手和四次挥手

因为HTTP是建立在TCP/IP通信协议上的用来传输数据的协议。
所以HTTP是稳定的,可靠的协议。
在学习TCP协议时,我们说TCP是可靠的协议,UDP是不可靠的协议。
其原理是因为TCP通信时会有一个三次握手四次挥手过程。

客户端服务器请准备为我通信(SYN)我准备好了(ACK=SYN+1),你呢(SYN)我也准备好了(ACK=SYN+1)客户端服务器

以上过程就是TCP在数据传输之前会先进行的一种面向连接的过程。俗称:三次握手。
首先,当客户端套接字链接服务器时,客户端会向服务器发送一个SYN码,意思差不多就是:你好,我要来给你通信了,你准备好了吗?
然后,这时,服务器收到客户端发送的SYN码,就会将SYN码+1,回送给客户端,也就是ACK码,同时还要再发送一个新的SYN码,代表说:我准备好了,你准备好发送了吗?
最后,客户端将服务器回送的新的SYN码+1,再发送给服务器。表示:我也准备好了,开始通信吧。
哈哈,是不是很奇妙的过程。
因为套接字是全双工的,所以当客户端和服务器之间数据通信完后,在断开连接时,又会执行四次挥手的操作,确保客户端和服务器的套接字发送和接收的功能都关闭。

客户端服务器客户端调用close(),关闭发送功能收到,此时我关闭接收功能我关闭发送功能我关闭接收功能客户端服务器

想一想为什么服务器关闭接收功能和关闭发送功能不能同时回送给客户端,要分为两次进行?

1.3 请求方法

根据 HTTP 标准,HTTP 请求可以使用多种请求方法。
HTTP1.0 定义了三种请求方法: GET, POSTHEAD方法。
HTTP1.1 新增了六种请求方法:OPTIONSPUTPATCHDELETETRACECONNECT方法。

序号 方法 描述
1 GET 请求指定的页面信息,并返回实体主体
2 HEAD 类似于 GET 请求,只不过返回的响应中没有具体的内容,用于获取报头
3 POST 向指定资源提交数据进行处理请求
4 PUT 从客户端向服务器传送的数据取代指定的文档的内容
5 DELETE 请求服务器删除指定的页面
6 CONNECT HTTP/1.1 协议中预留给能够将连接改为管道方式的代理服务器
7 OPTIONS 允许客户端查看服务器的性能
8 TRACE 回显服务器收到的请求,主要用于测试或诊断
9 PATCH 是对 PUT 方法的补充,用来对已知资源进行局部更新

1.4 状态码

HTTP状态码由三个十进制数字组成,第一个十进制数字定义了状态码的类型,后两个数字没有分类的作用。HTTP状态码共分为5种类型:

1** 信息,服务器收到请求,需要请求者继续执行操作
2** 成功,操作被成功接收并处理
3** 重定向,需要进一步的操作以完成请求
4** 客户端错误,请求包含语法错误或无法完成请求
5** 服务器错误,服务器在处理请求的过程中发生了错误

2. 模拟实现百度页面

百度的HTML文件以及上传至百度网盘,读者可以点击获取。
百度网盘: 点击获取.

import socket
import re

def service_client(new_socket,client_addr):
    # 接收客户端的请求头
    request = new_socket.recv(1024).decode("gbk")
    request_str = str(request)
    # 使用正则表达式,提取要访问的文件名字
    file_name = re.search(r"/[^ ]*",request_str)
    # 回送响应头和数据
    try:
        f = open("./baidu"+file_name.group(),"rb")
    except:
        if file_name.group() == "/":
            f = open("./baidu/index.html","rb")
            response_content = f.read()
            f.close()
            response_header = b"HTTP /1.1 200 OK\r\n"
            response_header += b"\r\n"
            new_socket.send(response_header)
            new_socket.send(response_content)
        else:
            response_header = b"HTTP /1.1 404 NOT FOUND\r\n"
            response_header += b"\r\n"
            response_content = "<h1> --- FILE NOT FOUND --- </h1>"
            new_socket.send(response_header)
            new_socket.send(response_content.encode("gbk"))
    else:
        response_content = f.read()
        f.close()
        response_header = b"HTTP /1.1 200 OK\r\n"
        response_header += b"\r\n"
        new_socket.send(response_header)
        new_socket.send(response_content)

    new_socket.close()

def main():
    # 1. 创建套接字
    main_socket = socket.socket()
    # 2. 绑定
    main_socket.bind(("",5678))
    # 3. 让套接字变成监听套接字
    main_socket.listen(128)
    while True:
        # 4. 等待客户端链接
        new_socket,client_addr = main_socket.accept()
        # 5. 为客户端服务
        service_client(new_socket,client_addr)
    # 关闭监听套接字
    main_socket.close()

if __name__ == '__main__':
    main()

将百度的HTML文件包解压到程序的当前路径下,运行上述代码就能实现模拟实现百度页面。
运行上述代码,当我们用浏览器访问127.0.0.1:5678,就能成功显示出百度的页面了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章