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'}
相关推荐
wyiyiyi2 小时前
【Web后端】Django、flask及其场景——以构建系统原型为例
前端·数据库·后端·python·django·flask
mit6.8242 小时前
[1Prompt1Story] 滑动窗口机制 | 图像生成管线 | VAE变分自编码器 | UNet去噪神经网络
人工智能·python
没有bug.的程序员2 小时前
JVM 总览与运行原理:深入Java虚拟机的核心引擎
java·jvm·python·虚拟机
甄超锋3 小时前
Java ArrayList的介绍及用法
java·windows·spring boot·python·spring·spring cloud·tomcat
AntBlack4 小时前
不当韭菜V1.1 :增强能力 ,辅助构建自己的交易规则
后端·python·pyqt
杜子不疼.6 小时前
《Python学习之字典(一):基础操作与核心用法》
开发语言·python·学习
myzzb7 小时前
基于uiautomation的自动化流程RPA开源开发演示
运维·python·学习·算法·自动化·rpa
TLuoQiu7 小时前
小电视视频内容获取GUI工具
爬虫·python
我叫黑大帅7 小时前
【CustomTkinter】 python可以写前端?😆
后端·python
胡耀超7 小时前
DataOceanAI Dolphin(ffmpeg音频转化教程) 多语言(中国方言)语音识别系统部署与应用指南
python·深度学习·ffmpeg·音视频·语音识别·多模态·asr