Python使用socket实现简易的http服务

在接触的一些项目中,有时为了方便可视化一些服务状态(请求数很少),那么很容易想到使用http服务来实现。但开源的web后端框架,例如flask,fastapi,django等略显沉重,且使用这些框架会有各种各样的限制,为了更加灵活的使用,可以自己通过Python自带的socket库来实现,下面是我简单实现的代码,贴在这里方便后续使用。

客户端代码:

python 复制代码
import os
import time
import json
import socket
import datetime
import traceback
from urllib import parse


ENCODING = "utf-8"


class CustomRouter:
    def __init__(self):
        self.router_dict = {}

    def register(self, url: str, method: str):
        if method not in ["GET", "POST"]:
            raise ValueError(f"method only support GET or POST, got {method}")

        key = f"{url}:{method}"
        if key in self.router_dict:
            raise ValueError(f"url:{url} method:{method} already registed")

        def _register(func):
            self.router_dict[key] = func
            return func

        return _register

    def has_register(self, url: str, method: str):
        key = f"{url}:{method}"
        return key in self.router_dict

    def request(self, url: str, method: str):
        key = f"{url}:{method}"
        func = self.router_dict[key]

        return func()


router = CustomRouter()


@router.register(url="/", method="GET")
def task():
    return "Hello World!!!"


@router.register(url="/get_project_info", method="GET")
def get_project_info():
    info = {"pwd": os.path.abspath(os.curdir),
            "encoding": ENCODING,
            "time": datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
    return json.dumps(info)


class CustomServer:
    def __init__(self, port: int = 5055, backlog: int = 2):
        self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.server.bind(("", port))
        self.server.listen(backlog)
        self.buff_size = 1024
        self.encoding = ENCODING
        self.router = router
        self.timeout = 1.0

    def parse_message(self, message: str):
        method, url_params, message = message.split(" ", maxsplit=2)
        res = parse.urlparse(url_params)
        url = res.path
        params = dict([i for i in parse.parse_qsl(res.query)])
        version, message = message.split("\r\n", maxsplit=1)
        return method, url, params, version

    def run(self):
        while True:
            conn, addr = self.server.accept()
            # conn.settimeout(self.timeout)
            with conn:
                try:
                    message = conn.recv(self.buff_size).decode(self.encoding)
                    t0 = time.perf_counter()
                    method, url, params, version = self.parse_message(message)

                    if not self.router.has_register(url, method):
                        conn.send("HTTP/1.1 501 Not Implemented\r\n\r\n".encode(self.encoding))
                        print(f"[{method}][501]client addr: {addr} request: {url}, time: {time.perf_counter() - t0:.5f}")
                        continue

                    res = self.router.request(url, method)
                    conn.send("HTTP/1.1 200 OK\r\n\r\n".encode(self.encoding))
                    conn.send(res.encode(self.encoding))
                    print(f"[{method}][200]client addr: {addr} request: {url}, time: {time.perf_counter() - t0:.5f}")
                except (BrokenPipeError, TimeoutError):
                    print(traceback.format_exc())
                except:
                    print(traceback.format_exc())
                    conn.send("HTTP/1.1 500 Inner Error\r\n\r\n".encode(self.encoding))
                    print(f"[{method}][500]client addr: {addr} request: {url}, time: {time.perf_counter() - t0:.5f}")


if __name__ == '__main__':
    s = CustomServer()
    s.run()

客户端代码:

python 复制代码
import requests


def main():
    for _ in range(50):
        res = requests.get("http://127.0.0.1:5055")
        print(res.status_code, res.text)

        res = requests.get("http://127.0.0.1:5055/get_project_info")
        print(res.status_code, res.json())


if __name__ == '__main__':
    main()

客户端请求时,服务端终端输出:

复制代码
[GET][200]client addr: ('127.0.0.1', 38054) request: /, time: 0.00002
[GET][200]client addr: ('127.0.0.1', 38058) request: /get_project_info, time: 0.00004
[GET][200]client addr: ('127.0.0.1', 38060) request: /, time: 0.00002
[GET][200]client addr: ('127.0.0.1', 38070) request: /get_project_info, time: 0.00004
[GET][200]client addr: ('127.0.0.1', 38080) request: /, time: 0.00002
[GET][200]client addr: ('127.0.0.1', 38096) request: /get_project_info, time: 0.00004
[GET][200]client addr: ('127.0.0.1', 38098) request: /, time: 0.00002
[GET][200]client addr: ('127.0.0.1', 38114) request: /get_project_info, time: 0.00009
[GET][200]client addr: ('127.0.0.1', 38120) request: /, time: 0.00003
[GET][200]client addr: ('127.0.0.1', 38122) request: /get_project_info, time: 0.00006
[GET][200]client addr: ('127.0.0.1', 38136) request: /, time: 0.00003
[GET][200]client addr: ('127.0.0.1', 38146) request: /get_project_info, time: 0.00006
[GET][200]client addr: ('127.0.0.1', 38160) request: /, time: 0.00003
[GET][200]client addr: ('127.0.0.1', 38172) request: /get_project_info, time: 0.00005
[GET][200]client addr: ('127.0.0.1', 38178) request: /, time: 0.00002
[GET][200]client addr: ('127.0.0.1', 38182) request: /get_project_info, time: 0.00005

客户端终端输出:

复制代码
200 Hello World!!!
200 {'pwd': '/home/wz/my_projects/others/socket_t', 'encoding': 'utf-8', 'time': '2025-01-14 00:20:01'}
200 Hello World!!!
200 {'pwd': '/home/wz/my_projects/others/socket_t', 'encoding': 'utf-8', 'time': '2025-01-14 00:20:01'}
200 Hello World!!!
200 {'pwd': '/home/wz/my_projects/others/socket_t', 'encoding': 'utf-8', 'time': '2025-01-14 00:20:01'}
200 Hello World!!!
200 {'pwd': '/home/wz/my_projects/others/socket_t', 'encoding': 'utf-8', 'time': '2025-01-14 00:20:01'}
200 Hello World!!!
200 {'pwd': '/home/wz/my_projects/others/socket_t', 'encoding': 'utf-8', 'time': '2025-01-14 00:20:01'}
200 Hello World!!!
200 {'pwd': '/home/wz/my_projects/others/socket_t', 'encoding': 'utf-8', 'time': '2025-01-14 00:20:01'}
200 Hello World!!!
200 {'pwd': '/home/wz/my_projects/others/socket_t', 'encoding': 'utf-8', 'time': '2025-01-14 00:20:01'}
200 Hello World!!!
200 {'pwd': '/home/wz/my_projects/others/socket_t', 'encoding': 'utf-8', 'time': '2025-01-14 00:20:01'}
相关推荐
aqi002 小时前
15天学会AI应用开发(九)利用Chroma持久化向量数据
人工智能·python·大模型·ai编程·ai应用
金銀銅鐵2 小时前
借助 Pygame 探索最大公约数的规律
python·数学·游戏
ServBay19 小时前
9 个 Python 第三方库推荐,不用 AI 都好像多出一个团队
后端·python
用户83562907805119 小时前
如何使用 Python 添加和管理 Excel 批注(完整示例)
后端·python
用户83562907805119 小时前
使用 Python 管理 Excel 工作表:创建、复制、删除与重命名
后端·python
荣码1 天前
LangGraph多Agent协作:3个Agent干活比1个强,但我踩了4个坑
java·python
用户8356290780512 天前
Python 操作 PDF 附件:添加、查看与管理指南
后端·python
宇宙之一粟2 天前
乐企版式文件生成平台
java·后端·python
学测绘的小杨3 天前
CompassFusion:一个从 GNSS 到 GNSS/INS 组合导航的独立工程包
python