当前位置: 首页 > news >正文

Python 封装 socket 为 [TCP/UDP/MULTICAST] 服务端

在新线程中创建 TCP/UDP/MULTICAST 协议的服务端套接字,接收客户端的连接请求或数据,并调用 on_recv 回调函数处理数据。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import socket
import threading
import multiprocessingclass ServerSocket:def __init__(self, *, protocol: str, port: int, group: str = '', on_recv: 'function'):"""服务端套接字在新线程中创建 TCP/UDP/MULTICAST 协议的服务端套接字,接收客户端的连接请求或数据,并调用 on_recv 回调函数处理数据。TCP 断开连接的情况:- TCP 正常断开+ 客户端主动断开连接+ 通信期间正常交换数据 (若服务端返回了响应, 则客户端应该接收响应)- TCP 连接已重置+ 客户端主动断开连接+ 服务端返回了响应,但客户端未接收- TCP 连接已终止+ 未通信完毕就已经断开了连接Args:protocol (str): 协议port (int): 端口号group (str, optional): 组播地址. Defaults to ''.on_recv (function, optional): 接收到数据时的回调函数, 参数为 (data: bytes, client_name: str). Defaults to None.Raises:ValueError: 无效的端口号, 应为 [1-65535]ValueError: 无效的协议类型, 应为 [TCP, UDP, MULTICAST]"""if port < 1 or port > 65535:raise ValueError(f'ServerSocket 无效的端口号 "{port}"')if protocol not in ['TCP', 'UDP', 'MULTICAST']:raise ValueError(f'ServerSocket 无效的协议类型 "{protocol}"')if protocol == 'MULTICAST' and not group:raise ValueError(f'ServerSocket 组播协议必须指定组播地址')if protocol != 'MULTICAST' and group:raise ValueError(f'ServerSocket 协议类型 "{protocol}" 请勿设置 group 参数')self.protocol = protocolself.port = portself.group = groupself.on_recv = on_recvself.sock: socket.socket | None = Noneself.tcp_sub_socks: list[socket.socket] = []self.thread: threading.Thread | None = Noneself.__active = Falsedef __str__(self) -> str:if self.protocol == 'MULTICAST':return f'ServerSocket({self.protocol}, {self.group}:{self.port})'return f'ServerSocket({self.protocol}, {self.port})'def __del__(self) -> None:self.close()def __create_socket(self) -> None:match self.protocol:case 'TCP':self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)self.sock.bind(('0.0.0.0', self.port))self.sock.listen(10)case 'UDP':self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)self.sock.bind(('0.0.0.0', self.port))case 'MULTICAST':self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)self.sock.bind(('0.0.0.0', self.port))self.sock.setsockopt(socket.IPPROTO_IP,socket.IP_ADD_MEMBERSHIP,socket.inet_aton(self.group) + socket.INADDR_ANY.to_bytes(4, byteorder='big'))def __send_back(self, client_addr: tuple[str, int], client_sock: socket.socket | None = None) -> 'function':def send_back(data: bytes):if self.protocol == 'TCP':return client_sock.sendto(data, client_addr)return self.sock.sendto(data, client_addr)return send_backdef __tcp_sub_thread(self, client_sock: socket.socket, client_addr: tuple[str, int]) -> None:while self.is_active():try:if not (data := client_sock.recv(1024)):print(f'{self} TCP 子线程 {client_addr} 正常断开')breakself.on_recv and self.on_recv(data=data,client_addr=client_addr,send_back=self.__send_back(client_addr, client_sock),)except ConnectionResetError:print(f'{self} TCP 子线程 {client_addr} 连接已重置')breakexcept ConnectionAbortedError:print(f'{self} TCP 子线程 {client_addr} 连接已终止')breakexcept Exception as e:if self.is_active():print(f'{self} TCP 子线程 {client_addr} 异常: \n{e}')breakif self.is_active(): # 断开或异常self.tcp_sub_socks.remove(client_sock)client_sock.close()def __main_thread(self) -> None:self.__active = Truewhile self.is_active():try:if self.protocol == 'TCP':client_sock, client_addr = self.sock.accept()self.tcp_sub_socks.append(client_sock)threading.Thread(target=self.__tcp_sub_thread, args=(client_sock, client_addr), daemon=True).start()else:data, client_addr = self.sock.recvfrom(1024)self.on_recv and self.on_recv(data=data,client_addr=client_addr,send_back=self.__send_back(client_addr),)except Exception as e:if self.is_active():print(f'{self} 主线程异常 : \n{e}')breakdef start(self, is_process: bool = False) -> bool:"""启动服务端将在新线程中运行,直到调用 close() 关闭,TCP 协议下会创建子线程处理 TCP 连接Args:is_process (bool, optional): 是否以子进程运行. Defaults to False.Returns:bool: 是否启动成功"""try:self.__create_socket()except Exception as e:print(f'{self} 创建失败: \n{e}')return Falseif is_process:self.thread = multiprocessing.Process(target=self.__main_thread, daemon=True)else:self.thread = threading.Thread(target=self.__main_thread, daemon=True)self.thread.start()return Truedef close(self) -> bool:"""关闭服务端Returns:bool: 是否关闭成功"""try:self.__active = Falseif self.protocol == 'TCP':for client_sock in self.tcp_sub_socks:client_sock.shutdown(socket.SHUT_RDWR)client_sock.close()self.tcp_sub_socks.clear()else:self.sock.shutdown(socket.SHUT_RDWR)self.sock.close()if isinstance(self.thread, multiprocessing.Process):self.thread.terminate()else:self.thread.join()except Exception as e:print(f'{self} 关闭失败: \n{e}')return Falsereturn Truedef is_active(self) -> bool:"""返回服务端是否处于活动状态Returns:bool: 是否处于活动状态"""return self.__activequit = False
def on_recv(data, client_addr, send_back):global quitprint(f'收到数据: {data} 来自 {client_addr}')send_back(b'ok')if data == b'q':quit = True# server.close()if __name__ == '__main__':from time import sleepserver = ServerSocket(protocol='TCP', port=60000, on_recv=on_recv)# server = ServerSocket(protocol='UDP', port=60000, on_recv=on_recv)# server = ServerSocket(protocol='MULTICAST', group='224.1.1.1', port=65000, on_recv=on_recv)server.start()while server.is_active():print('等待数据...')if quit:print('退出')server.close()sleep(60)

相关文章:

Python 封装 socket 为 [TCP/UDP/MULTICAST] 服务端

在新线程中创建 TCP/UDP/MULTICAST 协议的服务端套接字&#xff0c;接收客户端的连接请求或数据&#xff0c;并调用 on_recv 回调函数处理数据。 #!/usr/bin/env python # -*- coding: utf-8 -*- import socket import threading import multiprocessingclass ServerSocket:de…...

c++ STL库 unordered_map

#include <iostream #include <string #include <unordered_map int main() { // 创建一个 unordered_map std::unordered_map<std::string, int> wordCount; // 插入元素 wordCount["apple"] 1; wordCount["banana"] 2;// 使用 insert…...

【接口测试】任务1:登录接口

需要技能竞赛软件测试资料的同学们可s聊我&#xff0c;详细了解 任务实现要求 根据系统管理员—登录—接口API文档&#xff0c;编写接口测试用例&#xff0c;分别使用PostMan及JMeter进行接口测试&#xff0c;需要检查系统接口是否能正常工作&#xff0c;返回值是否正确&#…...

二、Spring Boot集成Spring Security之实现原理

Spring Boot集成Spring Security之实现原理 一、Spring Security实现原理概要介绍二、使用WebSecurityConfiguration向Spring容器中注册FilterChainProxy类型的对象springSecurityFilterChain1、未配置securityFilterChain过滤器链时使用默认配置用于生成默认securityFilterCha…...

基于深度学习的点云处理模型PointNet++学习记录

前面我们已经学习了Open3D&#xff0c;并掌握了其相关应用&#xff0c;但我们也发现对于一些点云分割任务&#xff0c;我们采用聚类等方法的效果似乎并不理想&#xff0c;这时&#xff0c;我们可以想到在深度学习领域是否有相关的算法呢&#xff0c;今天&#xff0c;我们便来学…...

Javascript Object.assgin()详解以及深浅拷贝

Object.assign() 方法是 JavaScript 中用于将所有可枚举属性的值从一个或多个源对象复制到目标对象的方法。它将返回目标对象。这是一种浅拷贝&#xff0c;也就是说&#xff0c;如果源对象中的属性是一个对象或数组&#xff0c;那么这个属性的引用将被复制&#xff0c;而不是对…...

Redis篇(应用案例 - UV统计)(持续更新迭代)

目录 一、HyperLogLog 二、测试百万数据的统计 一、HyperLogLog 首先我们搞懂两个概念&#xff1a; UV&#xff1a;全称Unique Visitor&#xff0c;也叫独立访客量&#xff0c;是指通过互联网访问、浏览这个网页的自然人。 1天内同一个用户多次访问该网站&#xff0c;只记录…...

解锁微信小程序新技能:ECharts动态折线图搭配WebSocket,数据刷新快人一步!

在微信小程序中&#xff0c;数据可视化展示越来越受到开发者的重视。本文将为您介绍如何在微信小程序中使用ECharts绘制折线图&#xff0c;并通过WebSocket实现实时更新图表数据。 一、准备工作 创建微信小程序项目 首先&#xff0c;我们需要创建一个微信小程序项目。如果您已…...

上交所服务器崩溃:金融交易背后的技术隐患暴露杭州BGP高防服务器43.228.71.X

一、上交所宕机事件始末 2024 年 9 月 27 日&#xff0c;上交所交易系统突发崩溃&#xff0c;这一事件犹如一颗巨石投入平静的湖面&#xff0c;引起了轩然大波。当天上午&#xff0c;众多投资者反馈券商交易出现延迟问题&#xff0c;随后上交所发布了《关于股票竞价交易出现异常…...

P4、P4D、HelixSwarm 各种技术问题咨询

多年大型项目P4仓库运维经验&#xff0c;为你解决各种部署以及标准工业化流程问题。 Perforce 官网SDPHelixCore GuideHelixSwarm GuideHelixSwarm Download...

Linux 应用层协议HTTP

文章目录 一、初始HTTP协议二、URL格式网络中怎么通过URL进行定位资源呢&#xff1f;编码和解码 三、HTTP的请求格式和响应格式HTTP的请求格式HTTP的响应格式HTTP的请求方法GET方法POST方法GET Vs PostHTTP的封装和分用文件流操作浏览器获得一个完整的网页流程 HTTP的状态码对3…...

Python和C++混淆矩阵地理学医学物理学视觉语言模型和算法模型评估工具

&#x1f3af;要点 优化损失函数评估指标海岸线检测算法评估遥感视觉表征和文本增强乳腺癌预测模型算法液体中闪烁光和切伦科夫光分离多标签分类任务性能评估有向无环图、多路径标记和非强制叶节点预测二元分类评估特征归因可信性评估马修斯相关系数对比其他准确度 Python桑…...

HTTP 协议的基本格式和 fiddler 的用法

HTTP协议格式 HTTP是⼀个⽂本格式的协议.可以通过Chrome开发者⼯具或者Fiddler抓包,分析HTTP请求/响应的细节. 抓包工具的使用 以Fiddler为例. • 左侧窗⼝显⽰了所有的HTTP请求/响应,可以选中某个请求查看详情. • 右侧上⽅显⽰了HTTP请求的报⽂内容.(切换到Raw标签⻚可以看…...

【计算机网络】详解UDP协议格式特点缓冲区

一、UDP 协议端格式 16 位 UDP 长度, 表示整个数据报(UDP 首部UDP 数据)的最大长度&#xff1b;如果16位UDP检验和出错&#xff0c;报文会被直接丢弃。 1.1、检验和出错的几种常见情况 数据传输过程中的比特翻转&#xff1a;在数据传输过程中&#xff0c;由于物理介质或网络设…...

网络安全cybersecurity的几个新领域

一、电力安全 同学们&#xff0c;今天我们来讨论一下为什么网络安全&#xff08;Cybersecurity&#xff09;和电力系统&#xff08;Power Systems&#xff09;这两个看似不同的领域会有交集。其实&#xff0c;这两个领域之间的联系非常紧密。以下我将从多个角度进行解释&#…...

android 原生加载pdf

implementation("androidx.pdf:pdf-viewer-fragment:1.0.0-alpha02") pdf加载链接...

MAE(平均绝对误差)和std(标准差)计算中需要注意的问题

一、MAE&#xff08;平均绝对误差&#xff09; 计算公式&#xff1a; yi​ 是第i个实际值y^​i​ 是第i个预测值 计算方法&#xff1a; MAE就是求实际值与预测值之间的误差&#xff0c;需要给出预测值和原始的实际值 二、std&#xff08;标准差&#xff09; 计算公式&#x…...

03实战篇:把握667分析题的阅读材料、题目

本节你将学习到&#xff1a; 如何快速识别阅读材料的有效信息如何把握题目的作答方向 在正式进入具体的实战之前&#xff0c;我想先来讲一讲如何利用给定阅读材料、如何分析题目来确保不偏题等基础性知识。 高效利用给定阅读材料的方法 根据博主的实战经验来看&#xff0c;阅…...

C++系列-多态

&#x1f308;个人主页&#xff1a;羽晨同学 &#x1f4ab;个人格言:“成为自己未来的主人~” 多态 多态就是不同类型的对象&#xff0c;去做同一个行为&#xff0c;但是产生的结果是不同的。 比如说&#xff1a; 都是动物叫声&#xff0c;猫是喵喵&#xff0c;狗是汪汪&am…...

基于C++和Python的进程线程CPU使用率监控工具

文章目录 0. 概述1. 数据可视化示例2. 设计思路2.1 系统架构2.2 设计优势 3. 流程图3.1 C录制程序3.2 Python解析脚本 4. 数据结构说明4.1 CpuUsageData 结构体 5. C录制代码解析5.1 主要模块5.2 关键函数5.2.1 CpuUsageMonitor::Run()5.2.2 CpuUsageMonitor::ComputeCpuUsage(…...

AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…...

【Linux】shell脚本忽略错误继续执行

在 shell 脚本中&#xff0c;可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行&#xff0c;可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令&#xff0c;并忽略错误 rm somefile…...

C++:std::is_convertible

C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...

深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法

深入浅出&#xff1a;JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中&#xff0c;随机数的生成看似简单&#xff0c;却隐藏着许多玄机。无论是生成密码、加密密钥&#xff0c;还是创建安全令牌&#xff0c;随机数的质量直接关系到系统的安全性。Jav…...

[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?

论文网址&#xff1a;pdf 英文是纯手打的&#xff01;论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误&#xff0c;若有发现欢迎评论指正&#xff01;文章偏向于笔记&#xff0c;谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...

LLM基础1_语言模型如何处理文本

基于GitHub项目&#xff1a;https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken&#xff1a;OpenAI开发的专业"分词器" torch&#xff1a;Facebook开发的强力计算引擎&#xff0c;相当于超级计算器 理解词嵌入&#xff1a;给词语画"…...

数据库分批入库

今天在工作中&#xff0c;遇到一个问题&#xff0c;就是分批查询的时候&#xff0c;由于批次过大导致出现了一些问题&#xff0c;一下是问题描述和解决方案&#xff1a; 示例&#xff1a; // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...

CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云

目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...

3403. 从盒子中找出字典序最大的字符串 I

3403. 从盒子中找出字典序最大的字符串 I 题目链接&#xff1a;3403. 从盒子中找出字典序最大的字符串 I 代码如下&#xff1a; class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...

3-11单元格区域边界定位(End属性)学习笔记

返回一个Range 对象&#xff0c;只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意&#xff1a;它移动的位置必须是相连的有内容的单元格…...