理解HTTP服务器工作的原理:
1.web服务器如何接收请求
2.web服务器如何定位资源
3.web服务器如何应答客户端
涵盖知识点
1.网络协议:
-
TCP协议:
–协议理论基础
–套接字通信 -
HTTP协议:
–协议理论基础
–请求报文结构
–应答报文结构
2.系统编程知识
- 类的抽象与继承
- 网络服务器的实现
- 多线程编程
- 网络请求多线程处理模型
章节分层:
一、认识传输层TCP协议
二、面向TCP协议的套接字服务端编程
三、认识应用层HTTP协议,理解这个协议是如何工作的
四、面向HTTP协议的服务器编程-将对HTTP报文进行拆解、理解并返回给客户端想要的内容
代码实践
1.1 实现TCP Server: 接收客户端的TCP连接。
1.2 实现StreamRequestHandler类: 进行连接处理、封装字节流网络请求处理功能,也就是对TCP连接进行处理。
2.1 BaseHTTPServer: 基础HTTP服务器,继承TCPServer。
2.2 BaseHTTPRequestHandler: 基于StreamRequestHandler,封装HTTP请求处理的基础功能。
3.1 SimpleHTTPServer: 将对外进行简单的服务,比如GET、POST方法、返回页面\数据。
3.2 SimpleHTTPRequestHandler: 实际实现HTTP请求(GET、POST)处理逻辑。、
一、认识传输层TCP协议
TCP/IP四层模型
通常将网络分成四个部分(TCP/IP四层模型):
为什么将网络分成这四个部分?主要是因为网络需要解决的问题繁多,因此将特定的功能交给某一层实现,比如网络接口层解决物理设备及特性的问题、网络层解决复杂网络环境问题、传输层解决通信可靠性的问题,应用层解决用户对接的问题。
1.1 TCP协议基础
TCP(Transmission Control Protocol: 传输控制协议)
TCP协议是计算机网络中非常复杂的一个协议:需要解决问题问题如下:
- 可靠传输:在复杂的网络环境中保证数据准确无误到达
- 流量控制:感知对方压力并控制流量
- 拥塞控制:感知网络压力并控制发送速度
TCP报文:包含两个部分:TCP首部+TCP数据报的数据。
TCP首部拥有非常多的字段,通过首部即可解决以上三个问题。
TCP数据报的数据包含来自应用层数据;HTTP数据在TCP数据报的数据里,通过TCP协议发送出去。
TCP是面向字节流的协议:不管应用层有哪些数据(utf8编码文字、图片、视频等),在进行TCP通信时都需要转成字节流才可以使用TCP协议进行传输。
应用场景:
- 微信/QQ等App消息发送接收
- 浏览器-服务器通信
- 其他可靠通信的场景
1.2 网络套接字与通信过程(是什么?如何进行网络通信?如何基于套接字去编程?)
什么是套接字?
当浏览器访问Web服务器的时候,浏览器相当于一个进程,Web服务器为一个进程,整个过程可看成进程间通信。计算机可以运行多个进程,这时就出现如何识别哪个进程在进行网络通信?
使用端口(Port)来标记不同的网络进程
端口(Port)使用16比特位表示(0~65535)
通过IP+端口就可以标记具体某台计算机的进程,IP+端口的组合称为套接字。
套接字(Socket)是抽象概念,表示TCP连接的一端,TCP连接是端到端通信,所以有两端也即两个套接字。
进行网络通信可以通过套接字可以进行数据发送与接收。
对套接字编程的时候分为:服务端编程和客户端编程。
服务端编程四步骤:创建套接字->绑定(bind)套接字->监听(listen)套接字->处理信息
客户端编程三步骤:创建套接字->连接(coonect)套接字->处理信息
1.3 套接字编程实践
httpserver/server.py
import socket
def server():
# 1.创建套接字
s = socket.socket()
# 2.绑定
HOST = '192.168.27.128'
PORT = 8999
s.bind((HOST, PORT))
# 3.监听
s.listen(5)
# 4.处理
while True:
c, addr = s.accept()
msg = c.recv(1024)
print('From client: %s; receive data: %s' % (addr, msg))
c.send(msg)
if __name__ == "__main__":
server()
httpserver/client.py
import socket
def client():
# 1.创建套接字
s = socket.socket()
# 2.连接
HOST = '192.168.27.128'
PORT = 8999
s.connect((HOST, PORT))
# 3.处理信息
s.send(b'Hello World!') # TCP是面向字节流的协议,在进行TCP通信时都需要转成字节流才可以使用TCP协议进行传输。
msg = s.recv(1024)
print('From server: %s' % msg)
if __name__ == "__main__":
client()
二、面向TCP协议的套接字服务端编程
通过本节你可以获得:TCP服务器的工作原理;TCP连接处理的原理;服务端多线程处理请求的模型。
2.1 实现TCPServer类: 接收客户端的TCP连接。
启动功能:serve_forever
处理请求功能: get_request、process_request、close_request
正常关闭功能: shutdown
基本属性:服务端地址、处理器Handler、套接字
httpserver/server/socket_server.py
import socket
class TCPserver:
def __init__(self, server_address, HandlerClass):
self.is_shutdown = False
self.server_address= None
self.HandlerClass = None # 处理请求的类
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 套接字socket,定义socket协议类和socket的类别,因为使用TCPServer,处理的信息属于字节流,所以是SOCK_STREAM
# 服务器的启动函数
def server_forever(self):
self.socket.bind(self.server_address)
self.socket.listen(10)
while not self.is_shutdown:
# 1.接收请求
request, client_address = self.get_request()
# 2.处理请求
try:
self.process_request(request, client_address)
except Exception as e:
print(e)
finally:
# 3.关闭连接
self.close_request(request)
# 接收请求
def get_request(self):
return self.socket.accept() # 阻塞式等待连接到来
# 处理请求
def process_request(self, request, client_address):
handler = self.HandlerClass(request, client_address)
handler.handler()
# 关闭请求
def close_request(self, request):
request.shutdown()
request.close()
# 关闭服务器
def shutdown(self):
self.is_shutdown = True
2.2 实现StreamRequestHandler类:进行连接处理、封装字节流网络请求处理功能,处理TCP连接的主要逻辑
网络请求处理器Handler类: 封装TCP连接处理逻辑
基本功能:
- 编码/解码功能:encode, decode
- 读/写/发消息功能:read/readline, write_content, send
httpserver/handler/base_handler.py
class BaseRequestHandler:
def __init__(self, server, request, client_address):
self.server = server
self.request = request
self.client_address = client_address
def handle(self):
pass
class StreamRequestHandler(BaseRequestHandler):
def __init__(self, server, request, client_address):
BaseRequestHandler.__init__(self, server, request, client_address)
self.wbuf = []
self.rfile = self.request.makefile('rb')
self.wfile = self.request.makefile('wb')
# 编码:字符串->字节码
def encode(self, msg):
if not isinstance(msg, bytes): # 判断是否是字节码
msg = bytes(msg, encoding='utf-8')
return msg
# 解码:字节码->字符串
def decode(self, msg):
if isinstance(msg, bytes):
msg = msg.decode()
return msg
# 读消息
def read(self, length):
msg = self.rfile.read(length)
return self.decode(msg)
# 读取一行,一行有个最大长度,http请求报文的最大长度
def readline(self, length=65536):
msg = self.rfile.readline(length).strip()
return self.decode(msg)
# 写消息,写到缓存中
def write_content(self, msg):
msg = self.encode(msg)
self.wbuf.append(msg)
# 从缓存中提取数据进行发送
def send(self):
for line in self.wbuf: # 往文件描述符写入
self.wfile.write(line)
self.wfile.flush()
self.wbuf = [] # 清空
def close(self):
self.wfile.close()
self.rfile.close()
2.3 多线程TCPServer: 提高服务端的处理性能
httpserver/server/socket_server.py
import threading
import socket
class TCPServer:
def __init__(self, server_address, handler_class):
self.is_shutdown = False
self.server_address = server_address
self.HandlerClass = handler_class
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 服务器的启动函数
def server_forever(self):
self.socket.bind(self.server_address)
self.socket.listen(10)
while not self.is_shutdown:
# 1.接收请求
request, client_address = self.get_request()
# 2.处理请求
try:
# self.process_request(request, client_address)
self.process_request_multithread(request, client_address)
except Exception as e:
print(e)
# 接收请求
def get_request(self):
return self.socket.accept()
# 处理请求
def process_request(self, request, client_address):
# 实例化
handler = self.HandlerClass(self, request, client_address)
# 调用handler进行请求处理
handler.handle()
# 3.关闭连接
self.close_request(request)
# 多线程处理请求
def process_request_multithread(self, request, client_address):
t = threading.Thread(target=self.process_request, args=(request, client_address))
t.start()
# 关闭请求
def close_request(self, request):
request.shutdown(socket.SHUT_WR)
request.close()
# 关闭服务器
def shutdown(self):
self.is_shutdown = True
2.4测试用例
import sys
sys.path.append('/home/httpserver')
import time
import socket
import threading
from server.socket_server import TCPServer
from handler.base_handler import StreamRequestHandler
class TestBaseRequestHandler(StreamRequestHandler):
def handle(self):
msg = self.readline()
print('Server recv msg:' + msg)
time.sleep(1)
self.write_content(msg)
self.send()
# 测试SocketServer(TCPServer)
class SocketServerTest:
def run_server(self):
tcp_server = TCPServer(('127.0.0.1', 8999), TestBaseRequestHandler)
tcp_server.server_forever()
# 客户端具体连接逻辑
def client_connect(self):
client = socket.socket()
client.connect(('127.0.0.1', 8999))
client.send(b'Hello TCPServer\n')
msg = client.recv(1024)
print('Client recv msg:' + msg.decode())
# 生成客户端
def gen_client(self, num):
clients = []
for i in range(num):
client_thread = threading.Thread(target=self.client_connect)
clients.append(client_thread)
return clients
def run(self):
server_thread = threading.Thread(target=self.run_server)
server_thread.start()
clients = self.gen_client(10)
for client in clients:
client.start()
server_thread.join() # 等待停止
for client in clients:
client.join() # 等待停止
if __name__ == "__main__":
SocketServerTest().run()
3 认识应用层HTTP协议
3.1 了解HTTP协议
HTTP(Hyper Text Transfer Protocol:超文本传输协议)
3.2 了解HTTP请求报文
请求行:请求方法,地址,HTTP版本
请求头:浏览器携带的信息,例如浏览器版本、支持的文本、编码、语言等。
请求内容:发送数据,与请求头间隔1行。
3.3 了解HTTP应答报文
状态行:HTTP版本,状态码,状态解释
应答头:服务器端信息,例如web服务器名,返回内容的数据编码/语言,内容长度
应答内容:长度跟Content-Length相等
4 面向HTTP协议的服务端编程
理解HTTP请求、应答报文
解析、封装HTTP报文
HTTP请求的处理过程(GET、POST)
4.1 实现基础的HTTP请求处理器
BaseHTTPRequestHandler类封装HTTP请求的基本功能:
- 解析/处理请求
handle: 请求的处理
parse_request: 请求的解析 - 返回结果。
write_header:写入应答头部
write_rseponse:写入应答内容
write_error:写入错误应答内容
# -*- encoding=utf-8 -*-
import sys
import logging
sys.path.append('/home/httpserver')
from handler.base_handler import StreamRequestHandler
logging.basicConfig(level=logging.DEBUG, format="%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s")
class BaseHTTPRequestHandler(StreamRequestHandler):
def __init__(self, server, request, client_address):
self.method = None
self.path = None
self.version = None
self.headers = None
self.body = None
StreamRequestHandler.__init__(self, server, request, client_address)
# 请求的处理
def handle(self):
try:
# 1.解析请求
if not self.parse_request():
return
# 2.方法执行(GET、POST)
method_name = 'do_' + self.method
if not hasattr(self, method_name):
return
method = getattr(self, method_name)
method() # 应答报文的封装
# 3.发送结果
self.send()
except Exception as e:
logging.exception(e)
# 解析请求头
def parse_headers(self):
headers = {
}
while True:
line = self.readline()
# 如果是空行,表示请求头已经结束
if line:
key, value = line.split(":", 1)
key = key.strip()
value = value.strip()
headers[key] = value
else:
break
return headers
# 解析请求
def parse_request(self):
# 1.解析请求行
first_line = self.readline()
words = first_line.split()
# 请求方法,地址,版本
self.method, self.path, self.version = words
# 2.解析请求头
self.headers = self.parse_headers()
# 3.解析请求内容
key = 'Content-Length'
if key in self.headers.keys():
# 请求内容的长度
body_length = int(self.headers[key])
self.body = self.read(body_length)
return True
4.2测试用例
4.3编写自定义HTTP应用之GET方法
HTTP请求方法的处理过程
HTML资源定位的基本原理
4.4编写自定义HTTP应用之POST方法