HTTP服务器原理及开发

理解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方法

猜你喜欢

转载自blog.csdn.net/qq_38900565/article/details/105623914