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'}
相关推荐
SSH_55231 小时前
【大模型】情绪对话模型项目研发
人工智能·python·语言模型
love530love1 小时前
【笔记】在 MSYS2(MINGW64)中安装 python-maturin 的记录
运维·开发语言·人工智能·windows·笔记·python
G皮T4 小时前
【Python Cookbook】文件与 IO(二)
python·i/o·io·文件·gzip·stringio·bytesio
封奚泽优4 小时前
使用Python绘制节日祝福——以端午节和儿童节为例
人工智能·python·深度学习
干啥都是小小白5 小时前
话题通信之python实现
python·机器人·ros
仟濹5 小时前
「数据采集与网络爬虫(使用Python工具)」【数据分析全栈攻略:爬虫+处理+可视化+报告】
大数据·爬虫·python·数据挖掘·数据分析
水银嘻嘻5 小时前
03 APP 自动化-定位元素工具&元素定位
python·appium·自动化
蹦蹦跳跳真可爱5896 小时前
Python----目标检测(《用于精确目标检测和语义分割的丰富特征层次结构》和R-CNN)
人工智能·python·深度学习·神经网络·目标检测·cnn
抽风的雨6106 小时前
【python深度学习】Day 42 Grad-CAM与Hook函数
开发语言·python·深度学习
Mikhail_G6 小时前
Python应用for循环临时变量作用域
大数据·运维·开发语言·python·数据分析