HTTP协议与静态web服务器开发

发布时间 2023-10-23 16:57:07作者: 七落安歌

HTTP协议与静态web服务器开发

一、HTTP协议概述

1、网址URL

网址又称为URL,URL的英文全称是uniform resource locator,即统一资源定位符,通俗理解就是网络资源地址

URL地址:https://www.itcast.com/18/1122/10/E178J2O4000189FH.html

2、URL的组成

image-20231017194356661

协议部分:https:// 、http://、ftp://

域名部分:www.itcast.com => 转换为IP地址(有了IP地址就能找到计算机)

资源路径部分: /18/1122/10/E178J2O4000189FH.html

3、HTTP协议

HTTP协议介绍

HTTP协议的全称是(HyperText Transfer Protocol),翻译过来就是超文本传输协议

超本文是指在非文本数据的基础上还包括了非文本数据,非文本数据有图片、音乐、视频等,而这些非文本数据会使用链接的方式进行加载显示,通俗来说超文本就是带有链接的文本数据也就是我们所说的网页数据

带有链接的数据/网页数据:

image-20231017212631273

HTTP协议的制作者是蒂姆·伯纳斯-李,1991年设计出来的,HTTP协议设计之前目的是传输网页数据的,现在允许传输任意类型的数据

传输HTTP协议格式的数据是基于TCP传输协议的,发送数据之前需要先建立连接。

TCP传输协议是用来保证网络传输数据的安全性的,HTTP协议是用来规定这些数据的具体形式

HTTP协议作用

HTTP协议规定的数据格式是浏览器格式和web服务器通信数据的格式,也就是说浏览器和web服务器通信需要HTTP协议。

浏览器访问web服务器的通信过程

通信效果图:

image-20231017213601046

  1. 通过DNS(域名解析服务器)将域名解析成IP地址
  2. 获取到DNS(域名解析服务器)返回的IP地址
  3. 通过第二步得到的ip地址,浏览器与web服务器建立连接 (基于TCP协议)
  4. 浏览器向web服务器发送HTTP请求数据
    • GET请求:获取服务器端数据
    • POST请求:推送数据到服务器端
  5. 获取请求资源(web服务器程序根据浏览器发送的请求从服务器主机中获取资源)
  6. 服务器主机将请求数据传递给web服务器
  7. web服务器返回HTTP响应数据给浏览器客户端

4、小结

  • HTTP协议是一个超文本传输协议
  • HTTP协议是基于TCP传输控制协议传输数据的
  • HTTP协议规定了浏览器和Web服务器的通信数据的格式

二、查看HTTP协议的通信过程

1、谷歌浏览器开发者工具

在谷歌浏览器中,使用快捷键F12或Ctrl + shift + I 调出开发者工具,或选择“视图 ->开发者->”开发者工具”

image-20231018080824773

  • element(元素):用于查看或修改HTML标签

  • console(控制台):执行JS代码

  • 源代码(sources):查看静态资源文件,断点调试JS代码

  • network(网络):查看http协议通信过程

提示:开发者工具还是查看网页布局和JS代码调试的利器

2、HTTP协议的通信过程

image-20231017223629251

注意:每一次浏览器和服务器的数据通讯,都是成对出现的即请求和响应,同时每一次请求和响应都必须符合HTTP协议的格式

3、谷歌浏览器开发者工具使用

第一步:打开开发者工具

第二步:使用network发送http请求

① 点击network(网络)标签

② 在浏览器地址输入百度的网址,就能看到请求百度首页的http的通信过程

③ 这里的每一项记录的都是请求加响应的过程

第三步:查看http协议的通信过程

请求头、请求头信息

image-20231018091129603

响应头、响应体信息

image-20231018091629900

4、小结

谷歌浏览器的开发者工具是查看http协议的通信过程利器,通过network标签选项可以查看每一次的请求和响应的通信过程。

Headers(标头)选项总共有三部分组成:

① General(常规):主要信息

② Response(响应标头):响应头

③ Request Headers(请求标头):请求头

Response(响应)选项是查看响应体信息的

三、HTTP请求报文和响应报文

1、HTTP请求报文

HTTP最常见的两种请求报文方式:

① GET方式请求报文 GET:获取Web服务器数据

② POST方式请求报文 POST :向Web服务器提交数据

2、GET请求报文格式

由三部分组成:请求行、请求头以及空行

---- 请求行 ----
GET /wp-content/uploads/2020/12/zm.svg HTTP/1.1  # GET请求方式 请求资源路径  HTTP协议的版本
---- 请求头 -----
Host: www.itcast.cn  # 服务器主机地址和端口号,默认是80端口 
Connection: keep-alive # 和服务器保持长连接
Upgrade-Insecure-Requests: 1 # 让服务器升级不安全请求,使用https请求
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36  # 用户代理商,也就是客户端代理商
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8  # 可接受的数据类型
Accept-Encoding: gzip, deflate # 可接受的压缩格式
Accept-Language: zh-CN,zh;q=0.9 # 可接受的语言
Cookie: pgv_pvi=1246921728; # 登陆用户的身份标识
---- 空行 ----

3、GET请求报文分析

GET / HTTP/1.1\r\n

Host: www.itcast.cn\r\n  
Connection: keep-alive\r\n
Upgrade-Insecure-Requests: 1\r\n
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36\r\n
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\n
Accept-Encoding: gzip, deflate\r\n
Accept-Language: zh-CN,zh;q=0.9\r\n
Cookie: pgv_pvi=1246921728; \r\n

\r\n  (请求头信息后面还有一个单独的’\r\n’不能省略)   =>  空白行
  • 注意:每一项数据都之间都使用 \r\n 进行结束

在HTTP请求中,每一项的结束后,其后面都要添加一个标签 \r\n (代表一行的结束)

在Windows系统中,换行符使用\n来实现。但是在Linux以及Unix系统中,换行符需要使用\r\n来实现

4、POST请求报文格式

POST由四部分组成:请求行、请求头、空行以及请求体组成

---- 请求行 ----
POST /xmweb?host=mail.itcast.cn&_t=1542884567319 HTTP/1.1 # POST请求方式 请求资源路径 HTTP协议版本
---- 请求头 ----
Host: mail.itcast.cn # 请求服务器的主机IP地址和端口号,默认为80端口
Connection: keep-alive # 与服务器保持长连接
Content-Type: application/x-www-form-urlencoded  # 告诉服务器的请求的数据类型
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36 # 客户端名称
---- 空行 ----
---- 请求体 ----
username=hello&pass=hello # 请求参数

5、POST请求报文分析

POST /xmweb?host=mail.itcast.cn&_t=1542884567319 HTTP/1.1\r\n
Host: mail.itcast.cn\r\n
Connection: keep-alive\r\n
Content-Type: application/x-www-form-urlencoded\r\n
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36\r\n
\r\n(请求头信息后面还有一个单独的’\r\n’不能省略)
username=hello&pass=hello
  • 注意:每项数据之间使用\r\n进行结束

6、GET与POST请求报文小结

  • 一个HTTP请求报文可以由请求行、请求头、空行以及请求体四部分组成

  • 请求行由三部分组成:请求方式、请求资源路径、HTTP协议版本(1.1或2.0)

  • GET方式的请求报文没有请求体,只有请求行、请求头和空行

  • POST请求方式可以由请求行、请求头、空行和请求体四部分组成

    注意:POST请求方式可以没有请求体,但是这种方式很少见

7、HTTP响应报文分析

image-20231018155938697

响应报文由四部分组成:响应行、响应头、空行和响应体

--- 响应行/状态行 ---
HTTP/1.1 200 OK # HTTP协议版本 状态码 状态描述
--- 响应头 ---
Server: Tengine # 服务器名称
Content-Type: text/html; charset=UTF-8 # 内容类型(响应的数据类型,image/pong)
Connection: keep-alive # 和客户端保持长连接
Date: Fri, 23 Nov 2018 02:01:05 GMT # 服务器响应的时间
--- 空行 ---
--- 响应体 ---
<!DOCTYPE html><html lang=“en”> …</html> # 响应给客户端的数据(html数据)
  • 响应头信息主要是告诉浏览器的客户端应该如何处理我们返回的数据

8、HTTP响应状态码

HTTP响应状态码是用于表示服务器响应状态的3位数字代码

状态码 说明
200 服务器成功处理了请求
400 错误的请求,请求地址或参数有误
404 请求的资源在服务器不存在
500 服务器内部源代码出现错误
301 重定向

四、搭建Python自带的静态Web服务器(测试)

1、静态Web服务器定义

静态Web服务器就是可以为发出请求的浏览器提供静态文档(html/css/js/图片/音频/视频)的程序

那么动态Web服务器是什么呢?

比如我们所浏览的百度新闻,每天的新闻数据都会发生变化,那么访问这个数据就是动态的。

2、搭建Python自带的静态Web服务器

windows系统创建方式:

  1. 创建一个文件夹,把所有的资源文件(html、css等文件)都放进这个文件夹中
  2. 在DOS窗口使用cd命令切换到此目录中
  3. 使用python -m命令创建静态Web服务器
python -m http.server 9000 # 9000为指定的端口号,默认为8000

-m表示运行包里面的模块,执行这个命令的时候,需要进入你自己指定静态文件的目录中,然后通过浏览器就能访问到对应的html文件了,这样一个静态的Web服务器就搭建好了。

访问:在浏览器中输入:http://127.0.0.1:9000

五、使用python开发自己的Web服务器

1、开发的步骤

Web服务器 = TCP服务器(七步走) + HTTP协议(请求+响应)

  1. 编写一个TCP服务器程序
  2. 获取浏览器发送的HTTP请求报文数据
  3. 获取固定页面数据,把页面数据组装成HTTP响应报文数据发送给浏览器
  4. HTTP响应报文数据发送完成之后,关闭服务于客户端的套接字

2、返回固定页面的数据

import socket

tcp_server_tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置端口复用
tcp_server_tcp.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
tcp_server_tcp.bind(('127.0.0.1', 8000))
tcp_server_tcp.listen(128)

while True:
    new_socket, ip_port = tcp_server_tcp.accept()
    content = new_socket.recv(4096).decode('gbk')
    print(content)
    # 接下来就是返回数据给浏览器客户端
    with open('index.html', 'rb') as f:  # 首先获取数据 超文本形式读取数据
        file_data = f.read()

    # 关键点:我们需要将以上的数据拼接为HTTP响应报文(响应行、响应头、空行和响应体)
    # 响应行
    response_line = 'HTTP/1.1 200 OK \r\n'
    # 响应头
    response_header = 'Server:PWB/1.1\r\n'
    # 空行
    # 响应体
    response_body = file_data
    # 组装HTTP响应数据 这里因为response_body在读取数据时候已经选取rb形式读取,所以不需要转换为二进制流数据了
    response_data = (response_line + response_header + '\r\n').encode('utf-8') + response_body
    new_socket.send(response_data)
    # 关闭新套接字对象
    new_socket.close()

image-20231018203510732

3、返回指定页面的数据

分析步骤:

  1. 获取用户请求资源的路径
  2. 根据请求资源的路径,读取指定文件的数据
  3. 组装指定文件的数据的响应报文,发送给浏览器
  4. 判断请求的文件在服务器端存不存在,组装404状态的响应报文,发送给浏览器

获取用户请求资源的路径:

request_list = client_request_data.split(" ", maxsplit=2)
request_path = request_list[1]  # /gdp.html
if request_path == "/":
    # 如果用户没有指定资源路径那么默认访问的数据是首页的数据
    request_path = "/index.html"

获取指定页面的数据:

# 读取指定文件数据
# 使用rb的原因是浏览器也有可能请求的是图片
with open("html/" + request_path, "rb") as file:
    # 读取文件数据
    file_data = file.read()

组装指定页面数据的响应报文:

# 响应行
response_line = "HTTP/1.1 200 OK\r\n"
# 响应头
response_header = "Server: PWS1.0\r\nContent-Type: text/html;charset=utf-8\r\n"
# 响应体
response_body = file_data
# 拼接响应报文数据
response_data = (response_line + response_header + "\r\n").encode("utf-8") + response_body
# 发送响应报文数据
client_socket.send(response_data)
client_socket.close()

组装404页面数据的响应报文:

try:
	# 打开指定文件,代码省略...
except Exception as e:
    response_line = 'HTTP/1.1 404 Not Found\r\n'
    response_header = 'Server: PWS1.0\r\nContent-Type: text/html;charset=utf-8\r\n'
    response_body = '<h1>非常抱歉,您当前访问的网页已经不存在了</h1>'.encode('utf-8')
    response_data = (response_line + response_header + '\r\n').encode('utf-8') + response_body
    # 发送404响应报文数据
    client_socket.send(response_data)
else:
    # 发送指定页面的响应报文数据,代码省略...
finally:
    client_socket.close()

案例:

import socket

tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 端口复用
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
tcp_server_socket.bind(('127.0.0.1', 8080))
tcp_server_socket.listen(128)
while True:
    new_socket, ip_port = tcp_server_socket.accept()
    # 接受客户端的数据 其实通过type类型可以知道,得到的是一个str类型的数据
    client_request_data = new_socket.recv(4096)
    # 新版的很多浏览器都带了一个自动刷新的功能(每隔一段时间不操作,浏览器就会自动刷新一次) =》发送一个空的数据包过来
    # 判断客户端发来的请求报文是否为空
    if client_request_data:
        # 如果浏览发送过来了数据,则我们对HTTP请求数据进行解析
        client_request_data = client_request_data.decode('utf-8')
        # print(type(client_request_data))  str类型 GET /show.html HTTP/1.1
        
        # 获取用户请求的资源页面 (str类型)
        request_data = client_request_data.split(' ', maxsplit=2)
        # 获取请求的资源路径
        request_path = request_data[1]
        print(request_path)

        # 解决域名直接访问首页的问题
        if request_path == '/':
            request_path = '/index.html'

        try:
            # 根据请求的资源路径打开文件
            with open('html' + request_path, 'rb') as f:
                file_data = f.read()
                # 将页面数据组装成HTTP响应报文数据发送给客户端
                # 响应行、响应头、空行和响应体
        except:
            # 如果以上文件找不到,则返回404错误信息,这里我使用了一个html页面
            with open('html/found.html', 'rb') as f:
                file_data = f.read()
            response_line = 'HTTP/1.1 404 NOT Found\r\n'
            response_header = 'Server:PWB/1.1\r\nContent-Type: text/html;charset=utf-8\r\n'
            response_body = file_data
            response_data = (response_line + response_header + '\r\n' ).encode('utf-8')+ response_body
            new_socket.send(response_data)
        else:
            response_line = 'HTTP/1.1 200 OK\r\n'
            response_header = 'Server:PWB/1.1\r\nContent-Type: text/html;charset=utf-8\r\n'
            # 空行不能忘
            response_body = file_data
            # 组装为HTTP响应体格式的报文数据
            response_data = (response_line + response_header + '\r\n').encode('utf-8') + response_body
            new_socket.send(response_data)
        finally:
            # 关闭新的套接字对象
            new_socket.close()

注意:还需要在html文件夹中放进一个命名为favicon.ico的图片小图标,效果如下图的篮球图标

image-20231019083750519