web服务器

14_http协议和静态web服务器

14_http协议和静态web服务器

http协议

什么是http协议?

1
2
    HyperText Transfer Protocol:超文本传输协议,由蒂姆·伯纳斯-李于1991年设计。指超越限制的文本或者超链接,比如:图片、音乐、视频、超链接等都属于超文本。原本目的是传输网页数据的,现在允许传输任意类型的数据。
    http协议基于 TCP 传输协议的,发送数据之前需要先建立连接。它规定了浏览器和 Web 服务器通信数据的格式,也就是说浏览器和web服务器通信需要使用http协议。

image-20230927182333643

url是什么?

1
2
3
4
5
6
7
8
9
10
11
Uniform Resoure Locator:统一资源定位符,网络资源地址,也就是常说的网址。

------------ 案例 1 ----------------------
https://news.163.com/18/1122/10/E178J2O4000189FH.html
1. 协议部分: https://、http://、ftp://
2. 域名部分: news.163.com (域名是IP地址别名,用点分割使用英文字母和数字组成,目的是方便记住某台主机IP地址)
3. 资源路径部分: /18/1122/10/E178J2O4000189FH.html
------------ 案例 2 ----------------------
https://news.163.com/hello.html?page=1&count=10
1. 查询参数部分: ?page=1&count=10
2. ? 后面的 page 表示第一个参数,后面的参数都使用 & 进行连接

get请求保文

1
2
3
4
5
6
7
8
9
10
11
12
13
---- 请求行 ----
GET / 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; # 登录用户的身份标识

---- 空行 ----
  • get请求原始报文(每项数据之间使用:\r\n)
1
2
3
4
5
6
7
8
9
10
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’不能省略)

post请求报文

1
2
3
4
5
6
7
8
9
10
---- 请求行 ----
POST /xmweb?host=mail.itcast.cn&_t=1542884567319 HTTP/1.1 # POST请求方式 请求资源路径 HTTP协议版本
---- 请求头 ----
Host: mail.itcast.cn # 服务器的主机地址和端口号,默认是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 # 请求参数
  • post请求原始报文(每项数据之间使用:**\r\n**)
1
2
3
4
5
6
7
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

响应报文

1
2
3
4
5
6
7
8
9
10
11
--- 响应行/状态行 ---
HTTP/1.1 200 OK # HTTP协议版本 状态码 状态描述
--- 响应头 ---
Server: Tengine # 服务器名称
Content-Type: text/html; charset=UTF-8 # 内容类型
Transfer-Encoding: chunked # 发送给客户端内容不确定内容长度,发送结束的标记是0\r\n, Content-Length表示服务端确定发送给客户端的内容大小,但是二者只能用其一。
Connection: keep-alive # 和客户端保持长连接
Date: Fri, 23 Nov 2018 02:01:05 GMT # 服务端的响应时间
--- 空行 ---
--- 响应体 ---
<!DOCTYPE html><html lang=“en”> …</html> # 响应给客户端的数据
  • 响应原始报文( 每项数据之间使用:\r\n)
1
2
3
4
5
6
7
8
HTTP/1.1 200 OK\r\n
Server: Tengine\r\n 
Content-Type: text/html; charset=UTF-8\r\n
Transfer-Encoding: chunked\r\n
Connection: keep-alive\r\n
Date: Fri, 23 Nov 2018 02:01:05 GMT\r\n
\r\n(响应头信息后面还有一个单独的’\r\n’不能省略)
<!DOCTYPE html><html lang=“en”> …</html>

http状态码

1
2
3
4
5
6
状态码  说明 
200    请求成功
307    重定向
400    错误的请求,请求地址或者参数有误
404    请求资源在服务器不存在
500    服务器内部源代码出现错误

https协议

https协议:安全的超文本传输协议

安全:涉及到数据加密

加密方式

  • 对称秘钥加密

加密需要通讯双方都保存同一份密钥,通过这份密钥进行加密和解密

对称加密的优势在于加解密速度快,但是安全性较低,密钥一旦泄露,所有的加密信息都会被破解。

  • 非对称秘钥加密

任何人都可以通过公钥进行信息加密,但是只有用户私钥的人才能完成信息解密

客户端用公钥加密,服务器端用私钥解密,这样密文被拦截也没有关系。

但是这种效率低,影响通信速度。如果公钥暴露,可能会被篡改。

  • 证书秘钥加密(https采用的方式)

公钥先提交到证书认证机构(添加签名,防伪造),然后客户端用可信任的公钥加密密文传输给服务端,从而防止公钥被篡改的问题。

静态web服务器

web服务器是什么?

1
web服务器是为发出请求的浏览器提供静态文档的程序。

搭建python自带的静态web服务器

1
2
3
4
5
python3 -m http.server 端口号
    端口号不指定默认是8000
    -m表示运行包里面的模块,执行这个命令的时候,需要进入你自己指定静态文件的目录,然后通过浏览器就能访问对应的 html文件了,这样一个静态的web服务器就搭建好了。

python用上面的命令就可以搭建静态web服务器了。如下图我直接将服务器上的文件夹发布出去,本机(客户端)就可以访问虚拟机(服务端)的静态资源了

image-20230927182341101

自己搞个静态web服务器玩玩

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
步骤:
1. 编写一个TCP服务端程序
2. 获取浏览器发送的http请求报文数据
3. 读取页面数据,把页面数据组装成HTTP响应报文数据发送给浏览器。
4. HTTP响应报文数据发送完成以后,关闭服务于客户端的套接字。
多线程/多任务
1. 当客户端和服务端建立连接成功,创建子线程,使用子线程专门处理客户端的请求,防止主线程阻塞(单任务,比如一个网络调试助手建立链接.但是一直不请求,这样程序就卡在那里了。)。
2. 把创建的子线程设置成为守护主线程,防止主线程无法退出。
o o 
1. 把提供服务的Web服务器抽象成一个类(HTTPWebServer)
2. 提供Web服务器的初始化方法,在初始化方法里面创建socket对象
3. 提供一个开启Web服务器的方法,让Web服务器处理客户端请求操作。
common line 
1. 获取执行python程序的终端命令行参数
2. 判断参数的类型,设置端口号必须是整型
3. 给Web服务器类的初始化方法添加一个端口号参数,用于绑定端口号

具体实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
import os
import socket
import threading

# handler client request
def handler_client_request(new_socket):
    # receive data,4kb
    recv_client_data = new_socket.recv(4096)
    # handle receive data length is zero
    if len(recv_client_data) == 0:
        new_socket.close()
        return
        # decode bin data
    recv_client_data = recv_client_data.decode('utf-8')
    print(recv_client_data)
    # calculae request path
    request_list = recv_client_data.split(" ", maxsplit=2)
    request_path = request_list[1]
    # handler / request,return default index.html
    if request_path == "/":
        request_path = "index.html"
    # handler 404
    # os.path.exists("static/"+request_path)
    try:
        # open user request resource file
        with open("static" + request_path, "rb") as file:
            # read file data
            file_data = file.read()
    except Exception as e:
        # handler resource file not exist
        # response line
        response_line = "HTTP/1.1 404 Not Found\r\n"
        # response head
        response_head = "Server: nginx\r\n"
        with open("static/error.html", "rb") as file:
            # read file data
            file_data = file.read()
        # reponse content
        response_body = file_data
        # response
        response_data = (response_line + response_head + "\r\n").encode("utf-8") + response_body
        # send data
        new_socket.send(response_data)
    else:
        # response line
        response_line = "HTTP/1.1 200 OK\r\n"
        # response head
        response_head = "Server: nginx\r\n"
        # reponse content
        response_body = file_data
        # response
        response_data = (response_line + response_head + "\r\n").encode("utf-8") + response_body
        # send data
        new_socket.send(response_data)
    finally:
        # close socket
        new_socket.close()

def main():
    # tcp server socket
    tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # port reuse,t system release port right now
    tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
    # bing port,default 8000
    tcp_server_socket.bind(('', 8000))
    # set listen num
    tcp_server_socket.listen(128)
    while True:
        # wait accept client request
        new_socket, ip_port = tcp_server_socket.accept()
        # multi thread handler many user request
        sub_thread = threading.Thread(target=handler_client_request,args=(new_socket,))
        # set daemon thread
        sub_thread.setDaemon(True)
        # start sub thread
        sub_thread.start()
        

if __name__ == '__main__':
    main()

修改为面向对象版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
import os
import socket
import sys
import threading

# web server
class HttpWebServer(object):
    # init server method
    def __init__(self, port):
        # tcp server socket
        tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # port reuse,t system release port right now
        tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
        # bing port,default 8000
        tcp_server_socket.bind(('', port))
        # set listen num
        tcp_server_socket.listen(128)
        # set object attribute
        self.tcp_server_socket = tcp_server_socket

    # start server method
    def start(self):
        while True:
            # wait accept client request
            new_socket, ip_port = self.tcp_server_socket.accept()
            # multi thread handler many user request
            sub_thread = threading.Thread(target=self.handler_client_request, args=(new_socket,))
            # set daemon thread
            sub_thread.setDaemon(True)
            # start sub thread
            sub_thread.start()

    # handler client request
    @staticmethod
    def handler_client_request(new_socket):
        # receive data,4kb
        recv_client_data = new_socket.recv(4096)
        # handle receive data length is zero
        if len(recv_client_data) == 0:
            new_socket.close()
            return
            # decode bin data
        recv_client_data = recv_client_data.decode('utf-8')
        print(recv_client_data)
        # calculae request path
        request_list = recv_client_data.split(" ", maxsplit=2)
        request_path = request_list[1]
        # handler / request,return default index.html
        if request_path == "/":
            request_path = "index.html"
        # handler 404
        # os.path.exists("static/"+request_path)
        try:
            # open user request resource file
            with open("static" + request_path, "rb") as file:
                # read file data
                file_data = file.read()
        except Exception as e:
            # handler resource file not exist
            # response line
            response_line = "HTTP/1.1 404 Not Found\r\n"
            # response head
            response_head = "Server: nginx\r\n"
            with open("static/error.html", "rb") as file:
                # read file data
                file_data = file.read()
            # reponse content
            response_body = file_data
            # response
            response_data = (response_line + response_head + "\r\n").encode("utf-8") + response_body
            # send data
            new_socket.send(response_data)
        else:
            # response line
            response_line = "HTTP/1.1 200 OK\r\n"
            # response head
            response_head = "Server: nginx\r\n"
            # reponse content
            response_body = file_data
            # response
            response_data = (response_line + response_head + "\r\n").encode("utf-8") + response_body
            # send data
            new_socket.send(response_data)
        finally:
            # close socket
            new_socket.close()

def main():
    print(sys.argv)
    # common line args length is equals 2? python3 + file_name + port
    if len(sys.argv) != 2:
        print("execute common: python3 xxx.py 8000")
        return
        # judge args is number
    if not sys.argv[1].isdigit():
        print("execute common: python3 xxx.py 8000")
        return
        # get port args
    port = int(sys.argv[1])
    # create web server
    web_server = HttpWebServer(port)
    # start web server
    web_server.start()

if __name__ == '__main__':
    main()

web前端开发基础