实现一个Web服务器(HTTP服务器)

本文详细介绍了如何实现一个Web服务器,从创建TCPServer类开始,包括网络请求处理器Handler的设计,实现了BaseHandler接口和字节流处理器BaseStreamHandler。接着,讨论了多线程处理模型以提升并发性能,并深入讲解了HTTP协议,涵盖了GET和POST方法的处理。最后,文章总结了已实现的功能并展望了未来可能的优化,如异常处理、状态码、长连接和性能提升等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

实现 TCPServer类:

  • 功能:
  1. 启动 server_start
  2. 处理请求 get_request (获取客户端的TCP连接)、 process_request(处理请求)、close_request(关闭TCP连接)
  3. 正常关闭 shutdown
  • 属性:
  1. 自己的套接字 --> 用于接收客户端请求
  2. 服务端地址
  3. 处理器类 Handler

server/myTCPServer

#! _*_ encoding=utf-8 _*_

import socket
import threading

from ThreadPool.myThreadPool import myThreadPool
from ThreadPool.myTask import AsyncTask
from ThreadPool.myTask import Task

class TCPServer:

    def __init__(self, server_addr, handler_class):
        self.server_addr = server_addr
        self.HandlerClass = handler_class # 处理请求的类
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.is_shutdown = False    # 关闭服务器标记
        self.thread_pool = myThreadPool()

    # 启动服务器
    def server_start(self):
        self.socket.bind(self.server_addr)
        self.socket.listen(10)
        # 启动线程池
        self.thread_pool.start()
        while not self.is_shutdown:
            # 1. 接收请求
            request, client_addr = self.get_request()
            # 2. 处理请求 --> 可能有异常
            try:
                # self.process_request(request, client_addr)
                # 开启一个新的线程处理请求
                self.process_request_multithread(request, client_addr)
            except Exception as e:
                print(e)
            # finally:
                # # 3. 关闭连接
                # self.close_request(request)
        self.thread_pool.join()

    # 接收请求
    def get_request(self):
        return self.socket.accept()

    # 处理请求
    def process_request(self, request, client_addr):
        handler = self.HandlerClass(self, request, client_addr) # self需要传递进去:表示HandlerClass服务于TCPServer
        handler.handle()
        # 3. 关闭连接
        self.close_request(request)

    # 多线程处理请求
    def process_request_multithread(self, request, client_addr):
        # t = threading.Thread(target=self.process_request,
        #                      args=(request, client_addr))
        # t.start()

        # 实现线程池处理请求
        task = Task(self.process_request, request, client_addr)
        self.thread_pool.put(task)

    # 关闭请求
    def close_request(self, request):
        request.shutdown(socket.SHUT_WR)
        request.close()

    # 关闭服务器
    def shutdown(self):
        self.is_shutdown = True

实现网络请求处理器 Handler类

封装TCP连接处理逻辑

实现接口类 BaseHandler

为了更好的抽象与分离,定义父类:BaseRequestHandler(接口)

  • BaseHandler定义属性:
  1. server 为哪个server服务
  2. request 具体的请求(socket object)
  3. client_address 客户端的socket
  • 接口函数:
  1. handle() 留给子类实现
class BaseRequestHandler:

    def __init__(self, server, request, client_addr):
        # 为server服务
        self.server = server
        self.request = request
        self.client_addr = client_addr

    # 接口 具体由子类实现
    def handle(self):
        pass

字节流处理器 BaseStreamHandler

  • 功能:
  1. 编码 encode / 解码 decode: 字节流 <==> 字符串
  2. 读 消息: read / readline
  3. 写 消息: write_content
  4. 发送 消息 send
  5. 关闭 close
  • 属性:
  1. 写缓存:先将字节流(用encode转换)写入缓存,在send出去

读消息:通过request将消息读出来:
为了方便请求的读和写,将请求连接分离成 读 / 写 两个文件描述符

对于read:直接调用socket对文件描述符的方法:read
对于readline,调用readline

class StreamRequestHandler(BaseRequestHandler):

    def __init__(self, server, request, client_addr):
        BaseRequestHandler.__init__(self, server, request, client_addr)

        """
            makefile(...) -> an I/O stream connected to the socket
            The arguments are as for io.open() after the filename, except the only
            supported mode values are 'r' (default), 'w' and 'b'.
        """
        # XXX refactor to share code?
        self.rfile = self.request.makefile('rb')
        self.wfile = self.request.makefile('wb')

        self.write_buffer = []

    # 编码:将字符串编码为字节码:
    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)

    # 读取一行消息, 65536=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.write_buffer.append(msg)

    # 发送消息:写完成以后,将buffer内容发送出去
    def send(self):
        for line in self.write_buffer:
            self.wfile.write(line)
        # Q: 怎么将消息发送出去? flush?
        self.wfile.flush()
        self.write_buffer = []  # 将缓存区清空

    def close(self):
        self.wfile.close()
        self.rfile.close()

测试

#! _*_ encoding=utf-8 _*_

import threading, socket, time

from server.myTCPServer import TCPServer
from handler.BaseHandler import StreamRequestHandler
from server.myHTTPServer import BaseHTTPServer
from handler.BaseHTTPHandler import BaseHTTPRequestHandler

class TestBaseRequestHandler(StreamRequestHandler):

    # 实现handle()接口的具体的处理逻辑
    def handle(self):

        # 实现 echo 功能
        # 测试是否可以处理并发连接
        msg = self.readline()
        print('Server receive msg: ' + msg)
        time.sleep(1)   # 模拟每个客户端处理都需要花费1s的时间
        self.write_content(msg)
        self.send()


# 测试SocketServer(TCPServer)
class SocketServerTest:

    def run_server(self):
        tcp_server = TCPServer(('127.0.0.1', 8888), TestBaseRequestHandler)
        tcp_server.server_start()

    # 实现客户端的具体连接逻辑
    def client_connect(self):
        client = socket.socket()
        client.connect(('127.0.0.1', 8888))
        client.send(b'Hello TCPServer\r\n')
        msg = client.recv(1024)
        print('Client receive msg: ' + msg.decode())

    # 生成客户端
    def gen_clients(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_clients(10)
        for client in clients:
            client.start()

        server_thread.join()
        for client in clients:
            client.join()


class BaseHTTPRequestHandlerTest:

    def run_server(self):
        BaseHTTPServer(('127.0.0.1', 9999), BaseHTTPRequestHandler).server_start()

    def run(self):
        self.run_server()

if __name__ == '__main__':
    SocketServerTest().run()
    # BaseHTTPRequestHandlerTest().run()
  • 问题: TCPServer只有一个主线程,可以接收、处理Client的请求,但只能串行,并发度不高:

  • 解决:多线程模型,对每个请求创建一个新的线程处理

  • 问题:创建和消耗线程由时间、资源的开销

  • 优化:使用线程池模型:在启动TCPServer的同时启动线程池,每次新的请求提交给线程池处理

  • ERROR: server/myTCPServer/server_start()
    在这里插入图片描述要将关闭连接的操作,放到process_request中去,也就是新的线程执行逻辑中去,

并发:网络服务器的多线程处理模型

问题:新的Client连接会被阻塞
原因:TCPServer只有一个主线程进行处理请求
改进:

实现HTTP协议服务器

HTTP (HyperText Transfer Protocol)

地址格式:<协议>://<网站域名>:<端口>/<文件路径>

Web 2.0 操作方法:

GET 获取指定的服务端资源
POST 把数据提交给服务端
DELETE 删除指定的服务端资源
UPDATE 更新指定的服务端资源

  • Web 服务器工作流程:
    在这里插入图片描述

HTTP 请求报文

在这里插入图片描述

  • 格式:
    请求行:只有唯一一行
    请求头: 请求的附加信息(请求设备信息),Key:Value 键值对格式
    Content-Length: 请求内容长度(字节)
    <空行>
    请求内容(可选):发送数据(提交给服务器后台)

HTTP 应答报文

在这里插入图片描述

  • 格式:

状态行:
应答头:
Content-Length: 应答内容长度
<空行>
应答内容:

  • 状态行:状态码
    在这里插入图片描述
  • 请求头与应答头统称为消息头: 附加信息
    在这里插入图片描述

实现基础的HTTP请求处理器

  • 功能:
  1. 解析请求
  2. 处理请求 parse_request
  3. 返回结果 : write_header, write_response, write_error

编写自定义HTTP应用

GET方法

POST 方法

将数据提交到服务端进行处理

总结

在这里插入图片描述

展望

完善异常机制

状态码

日志

长连接

安全 https

性能

引入线程池
引入事件驱动引擎(epoll、select)IO复用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值