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

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

客户端代码:

import os
import time
import json
import socket
import datetime
import traceback
from urllib import parse
from concurrent.futures import ThreadPoolExecutor


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, num_workers: 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 = 60
        self.thread_pool = ThreadPoolExecutor(max_workers=num_workers)

    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 task(self, conn: socket.socket, addr):
        conn.settimeout(self.timeout)
        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}")
                conn.close()
                return

            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}")
        finally:
            conn.close()

    def run(self):
        while True:
            conn, addr = self.server.accept()
            self.thread_pool.submit(self.task, conn, addr)


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

客户端代码:

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'}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/954938.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

解析OVN架构及其在OpenStack中的集成

引言 随着云计算技术的发展,虚拟化网络成为云平台不可或缺的一部分。为了更好地管理和控制虚拟网络,Open Virtual Network (OVN) 应运而生。作为Open vSwitch (OVS) 的扩展,OVN 提供了对虚拟网络抽象的支持,使得大规模部署和管理…

解密AIGC三大核心算法:GAN、Transformer、Diffusion Models原理与应用

在当今数字化时代,人工智能生成内容(AIGC)技术正以前所未有的速度改变着我们的生活和工作方式。从创意无限的文本生成,到栩栩如生的图像创作,再到动听的音乐旋律,AIGC的魔力无处不在。而这一切的背后&#…

艾体宝干货丨网络故障排除基本指南

一、确保网络可视性以有效排除故障 有效的网络故障排除要求对穿越网络的数据具有完全的可见性,以便迅速识别和解决潜在问题。本指南深入探讨了一种结构化的网络分析方法,旨在提高故障排除的效率。首先,提出正确的问题至关重要,它…

汽车免拆诊断案例 | 2007 款法拉利 599 GTB 车发动机故障灯异常点亮

故障现象  一辆2007款法拉利599 GTB车,搭载6.0 L V12自然吸气发动机(图1),累计行驶里程约为6万km。该车因发动机故障灯异常点亮进厂检修。 图1 发动机的布置 故障诊断 接车后试车,发动机怠速轻微抖动,…

浪潮海岳 UploadListFile文件上传致RCE漏洞

一、漏洞简介 浪潮云财务系统的/cwbase/EP/ListContent/UploadListFile.ashx接口存在任意文件上传漏洞,未经身份验证的攻击者可以通过该漏洞上传恶意脚本文件,从而控制目标服务器。 二、漏洞影响 三、网络测绘: fofa: body"/cwbase/w…

高等数学学习笔记 ☞ 不定积分的积分法

1. 第一换元积分法 1. 基础概念:形如的过程,称为第一换元积分法。 2. 核心思想:通过对被积函数的观察(把被积函数的形式与积分表的积分公式进行比较),把外部的部分项拿到的内部(求原函数), 然后进行拼凑,…

Spring Boot 整合 Shiro详解

文章目录 Spring Boot 整合 Shiro详解一、引言二、整合步骤1、创建项目并引入依赖2、配置Shiro2.1、自定义Realm2.2、配置SecurityManager和ShiroFilterFactoryBean 三、使用示例四、总结 Spring Boot 整合 Shiro详解 一、引言 在现代的Web应用开发中,用户认证和授…

win10 Outlook(new) 企业邮箱登录 登录失败。请在几分钟后重试。附移除办法

windows系统经常弹出使用Outlook(new),自动切过去。 但是登录企业的内网邮箱,折腾了好几次都使用不了。排查网络等问题,在社区找到了答案。 推出一年多不支持企业账户,所以之前的折腾都是浪费时间。 因为这个答案不太…

tomcat状态一直是Exited (1)

docker run -di -p 80:8080 --nametomcat001 你的仓库地址/tomcat:9执行此命令后tomcat一直是Exited(1)状态 解决办法: 用以下命令创建运行 docker run -it --name tomcat001 -p 80:8080 -d 你的仓库地址/tomcat:9 /bin/bash最终结果 tomcat成功启动

Golang Gin系列-1:Gin 框架总体概述

本文介绍了Gin框架,探索了它的关键特性,并建立了简单入门的应用程序。在这系列教程里,我们会探索Gin的主要特性,如路由、中间件、数据库集成等,最终能使用Gin框架构建健壮的web应用程序。 总体概述 Gin是Go编程语言的…

实现linux硬盘smart检测

一、下载交叉编译libatasmart库 下载链接:https://www.linuxfromscratch.org/blfs/view/svn/general/libatasmart.html libatasmart库编译依赖libudev库,交叉编译器前先准备依赖的libudev: 设置libudev的环境变量,并通过configure编译文件生…

【GIS操作】使用ArcGIS Pro进行海图的地理配准(附:墨卡托投影对比解析)

文章目录 一、应用场景二、墨卡托投影1、知识点2、Arcgis中的坐标系选择 三、操作步骤1、数据转换2、数据加载3、栅格投影4、地理配准 一、应用场景 地理配准是数字化之前必须进行的一项工作。扫描得到的地图数据通常不包含空间参考信息,需要通过具有较高位置精度的…

模型 多元化思维(系统科学)

系列文章分享模型,了解更多👉 模型_思维模型目录。融合多学科知识,全面解决问题。 1 多元化思维模型的应用 1.1 完美日记的私域流量运营 完美日记作为美妆行业的新兴品牌,通过多元化的思维模型在私域流量运营中取得了显著成功。…

剧本杀门店预约系统开发,门店如何走下去?

近几年来,剧本杀行业经历了大浪淘金,行业进入到创新发展时期,如何在市场中占领一席之地成为了商家探讨的问题。 剧本杀作为一种社交游戏方式,深受年轻人的关注,不仅可以体验游戏的乐趣,还可以满足各种社交…

openharmony标准系统方案之瑞芯微RK3568移植案例

标准系统方案之瑞芯微RK3568移植案例 ​本文章是基于瑞芯微RK3568芯片的DAYU200开发板,进行标准系统相关功能的移植,主要包括产品配置添加,内核启动、升级,音频ADM化,Camera,TP,LCD&#xff0c…

Linux的常用命令(三)

目录 六、网络通信命令 1.网络通信命令ping 2.网络通信命令ifconfig 七、系统命令 1. 系统命令shutdown 2. 系统命令reboot 八、vi编辑器 六、网络通信命令 1.网络通信命令ping 命令名称:ping 命令所在路径:/usr/sbin/ping 执行权限&#xff…

STM32+W5500+以太网应用开发+003_TCP服务器添加OLED(u8g2)显示状态

STM32W5500以太网应用开发003_TCP服务器添加OLED(u8g2)显示状态 实验效果3-TCP服务器OLED1 拷贝显示驱动代码1.1 拷贝源代码1.2 将源代码添加到工程1.3 修改代码优化等级1.4 添加头文件路径1.5 修改STM32CubeMX工程 2 修改源代码2.1 添加头文件2.2 main函…

Pytorch|YOLO

🍨 本文为🔗365天深度学习训练营中的学习记录博客🍖 原作者:K同学啊 一、 前期准备 1. 设置GPU 如果设备上支持GPU就使用GPU,否则使用CPU import torch import torch.nn as nn import torchvision.transforms as transforms im…

2025.1.15——六、SQL结构【❤sqlmap❤】

一、打开靶机,整理已知信息 查看页面信息,提示”MySQL结构”,所以为sql注入,两种思路:①手工注入;②sqlmap 二、手工注入解题 step 1:查看注入类型 键入:1 键入:1键入…

螺旋矩阵探讨

文章目录 54.螺旋矩阵59.螺旋矩阵II 54.螺旋矩阵 59.螺旋矩阵 II 54.螺旋矩阵 总体的思路分析: 顺时针,先遍历右边,再下面,再往左,再向上,然后再缩小一圈范围即可 原本的代码情况 class Solution:def spi…