基于Socketserver+ThreadPoolExecutor+Thread构造的TCP网络实时通信程序

目录

介绍:

源代码:

Socketserver-服务端代码

Socketserver客户端代码:


介绍:

socketserver是一种传统的传输层网络编程接口,相比WebSocket这种应用层的协议来说,socketserver比较底层,socketserver的网络通信逻辑与收发、传输的数据格式与都要由开发者自己来定义,适合用来学习网络底层通信逻辑。我采用Python脚本来编程Socketserver的接口,我在下面放出源代码。

源代码:

我先讲一下我实现的转发模型,是C/S架构,不是P2P,由服务端中转客户端的发送消息这样。

Socketserver-服务端代码

import json
import socketserver
import struct
from threading import Thread
from concurrent.futures import ThreadPoolExecutorfrom threading import Lockdef send_byte(conn,msg):msg__bs_len = len(msg)msg_bs_len_bs = struct.pack('i',msg__bs_len)conn.sendall(msg_bs_len_bs)conn.sendall(msg)def recv_byte(conn):msg_recv_len_bs = conn.recv(4)msg_recv_len = struct.unpack('i', msg_recv_len_bs)[0]msg_recv = conn.recv( msg_recv_len )return msg_recvdef send(conn,msg):msg_json = json.dumps(msg)msg_bs = msg_json.encode('utf-8')msg_bs_len = len(msg_bs)msg_bs_len_pack=(struct.pack('i', msg_bs_len))conn.sendall(msg_bs_len_pack)conn.sendall(msg_bs)def recv_name(conn):name_len_bs = conn.recv(4)name_len = struct.unpack('i', name_len_bs)[0]name_bs = conn.recv(name_len)name = name_bs.decode('utf-8')return namedef recv(conn):msg_len_bs = conn.recv(4)msg_len = struct.unpack('i', msg_len_bs)[0]msg_bs = conn.recv(msg_len)msg = msg_bs.decode('utf-8')msg = json.loads(msg)return msgclass MyRequestHandler(socketserver.BaseRequestHandler):client_dict = {} #{address_port:address_port,sk_conn:conn}name_list = []stor_user_list = []retr_user_list = []lock = Lock()def handle(self):conn = self.requestaddress_port = self.client_addressclient_name = recv_name(conn)try:with ThreadPoolExecutor() as t:future = t.submit(handle_is_newuser,address_port,conn,client_name)def broadcast_welcome(future):is_new = future.result()if is_new:for key,value in MyRequestHandler.client_dict.items():sk_conn = value['sk_conn']send(sk_conn, f"系统消息: 【{client_name}】 加入了群聊,输入/help获取命令")future.add_done_callback(broadcast_welcome)except Exception as e:print ('出现异常:',e)while 1:msg_dict = recv(conn)print (msg_dict)msg = msg_dict['msg']name = msg_dict['name']try:if msg.upper() == 'Q':MyRequestHandler.name_list.remove(client_name)del MyRequestHandler.client_dict[name]for key, value in MyRequestHandler.client_dict.items():sk_conn = value['sk_conn']print (f'【{name}】退出了群聊')send(sk_conn, f'【{name}】退出了群聊')conn.close()elif msg == 'client/all':send(conn,f'在线用户列表:{MyRequestHandler.name_list}')elif msg == '/help':text ='查看在线用户:client/all\n私聊:/chat [对方名字] [消息内容]\n退出群聊:[q] or [Q]\n向对方传输文件:/stor [对方名字] [本地文件路径]\n显示递归目录树:/tree [对方名字] [远端目录]'send(conn,text)elif msg.lstrip().startswith('/tree_content'):try:parts = msg.split(' ',2)ip_or_name = parts[1]if ip_or_name == name:send(conn,'请指定对方名字')continueif ip_or_name in MyRequestHandler.name_list:values = MyRequestHandler.client_dict[ip_or_name]pri_conn = values['sk_conn']send(pri_conn,msg_dict)except Exception as e:print ('命令执行错误',e)elif msg.lstrip().startswith('/tree'):parts = msg.split(' ',2)ip_or_name = parts[1]if ip_or_name == name:send(conn, '请指定对方名字')continueif ip_or_name in MyRequestHandler.name_list:values = MyRequestHandler.client_dict[ip_or_name]remote_conn = values['sk_conn']send(remote_conn,msg_dict)continueelif msg.lstrip().startswith('stor')  or  msg.lstrip().startswith('retr') :print ('第一次文件传输交互')msg_bytes = recv_byte(conn)parts = msg.split(' ',3)remote_name= parts[1]client_dict_value = MyRequestHandler.client_dict[remote_name]remote_conn = client_dict_value['sk_conn']cmd = parts[0]send(remote_conn,msg_dict)if cmd == '/stor':print('进来了')send_byte(remote_conn,msg_bytes)print (msg_bytes)print ('发送成功')continueelse:for key, value in MyRequestHandler.client_dict.items():sk_conn = value['sk_conn']send(sk_conn, msg_dict)except Exception as e:print ('意外报错:',e)def handle_is_newuser(address_port,conn,client_name):dict_addr_conn = {}with MyRequestHandler.lock:if client_name in MyRequestHandler.name_list:returnelse:dict_addr_conn['address_port'] = address_portdict_addr_conn['sk_conn'] = connMyRequestHandler.client_dict[client_name] = dict_addr_connMyRequestHandler.name_list.append(client_name)return Trueif __name__ == '__main__':server = socketserver.ThreadingTCPServer(('127.0.0.1', 12345), MyRequestHandler)print("服务器正在运行...")server.serve_forever()

Socketserver客户端代码:
 

import json
import os
import socket
import struct
from threading import Thread
import sys
import  timename = ''
stor_user_list=[]def send_byte(conn,msg):msg__bs_len = len(msg)msg_bs_len_bs = struct.pack('i',msg__bs_len)conn.sendall(msg_bs_len_bs)conn.sendall(msg)def recv_byte(conn):msg_recv_len_bs = conn.recv(4)msg_recv_len = struct.unpack('i', msg_recv_len_bs)[0]msg_recv = conn.recv( msg_recv_len)return msg_recvdef send_name(conn):global namename = input('请取个名字吧:')name_bs = name.encode('utf-8')name_len = len(name_bs)conn.sendall(struct.pack('i', name_len))conn.sendall(name_bs)def send_handle(conn,name_msg):name_msg_json = json.dumps(name_msg)name_msg_json_bs = name_msg_json.encode('utf-8')name_msg_json_bs_len = len(name_msg_json_bs)name_msg_json_bs_len_pack = struct.pack('i', name_msg_json_bs_len)conn.sendall(name_msg_json_bs_len_pack)conn.sendall(name_msg_json_bs)def send(conn):global stor_user_listwhile True:name_msg = {}msg = input()name_msg['name'] = namename_msg['msg'] = msgtry:if msg.upper() == 'Q':# name_msg_json = json.dumps(name_msg)# msg_bs = name_msg_json.encode('utf-8')# msg_len = len(msg_bs)# conn.sendall(struct.pack('i', msg_len))# conn.sendall(msg_bs)send_handle(conn,name_msg)print ('我退出了群聊!')conn.close()sys.exit()if  str(msg.lstrip()).startswith('/stor') or  str(msg.lstrip()).startswith('/retr') :print('主动发起文件传输(A端)')parts = msg.split(' ', 3)command = parts[0]remote_name = parts[1]localpath = parts[2]# name_msg_json = json.dumps(name_msg)# msg_json_bs = name_msg_json.encode('utf-8')## msg_json_bs_len = len(msg_json_bs)# msg_json_bs_len_pack = struct.pack('i', msg_json_bs_len)## conn.sendall(msg_json_bs_len_pack)# conn.sendall(msg_json_bs )if '/stor' in command:name_byte = {}name_byte['name'] = namename_byte['msg'] = msgsend_handle(conn,name_byte)with open(localpath, mode='rb') as read_file:bytes = read_file.read()print('开始发送文件')send_byte(conn,bytes)print('文件发送成功')sys.stdout.write(f'{name}>>')sys.stdout.flush()continuesend_handle(conn,name_msg)sys.stdout.write(f'{name}>>')sys.stdout.flush()except Exception as e:print('异常报错:', e)sys.exit()def recv_handle(conn):msg_len_pack = conn.recv(4)msg_bs_len = struct.unpack('i', msg_len_pack)[0]msg_bs = conn.recv(msg_bs_len)msg_dict_json = msg_bs.decode('utf-8')msg_dict = json.loads(msg_dict_json)return msg_dictdef recv(conn):global stor_user_listwhile True:try:msg_dict = recv_handle(conn)sys.stdout.write('\r' + ' ' * 100 + '\r')  # 覆盖当前行sys.stdout.flush()if isinstance(msg_dict,list):print (msg_dict)elif isinstance(msg_dict,str):#由服务器发送的消息,因此无需以字典格式传输print (msg_dict)elif isinstance(msg_dict,dict):msg = msg_dict['msg']name_msg = msg_dict['name']print (name_msg)if msg.lstrip().startswith('/chat'):parts = msg.split(' ', 2)pri_msg = parts[2]print(f'{name_msg}>>{name} {pri_msg}')if msg.lstrip().startswith('/tree'):parts = msg.split(' ', 2)local_path = parts[2]tree =''def list_tree(path,tree,depth=1):dir_name = os.path.basename(path)tree += str(depth * '|-----')+str(dir_name).strip() + '\n'file_list = os.listdir(path)for file in file_list:filepath = os.path.join(path,file)if os.path.isdir(filepath):tree = list_tree(filepath,tree,depth+1)if os.path.isfile(filepath):tree += str(depth * '|-----') + '|-----' + file + '\n'return treedir_tree =list_tree(local_path,tree)dir_tree_full = '\n' + dir_treeprint (dir_tree_full)msg_dir_tree = {}msg_dir_tree['name'] = namemsg_dir_tree['msg'] = dir_tree_fullsend_handle(conn,msg_dir_tree)if name_msg != name and msg.upper() != 'Q' and  not msg.lstrip().startswith('/chat') and not  msg.lstrip().startswith('stor')  and not msg.lstrip().startswith('retr'):print(f'{name_msg}>> {msg}')if msg.lstrip().startswith('stor') or msg.lstrip().startswith('retr'):msg_bytes = recv_byte(conn)parts = msg_dict['msg'].split(' ',3)command = parts[0]local_path = parts[3]if  '/stor' in command:with open(local_path, mode='wb') as writefile:print('开始文件传输(B端)',flush=True)writefile.write(msg_bytes)writefile.flush()os.fsync(writefile.fileno())print ('传输完毕', flush=True)sys.stdout.write(f'{name}>>')sys.stdout.flush()except Exception as e:print ('接收消息出错:',e)if __name__ == '__main__':try:sk = socket.socket()sk.connect(('127.0.0.1', 12345))except Exception as e:print ('socket连接失败',e)sys.exit()send_name(sk)receiver = Thread(target=recv, args=(sk,), daemon=True)receiver.start()send(sk)sk.close()

定义的功能:

查看在线用户:client/all
私聊:/chat [对方名字] [消息内容]
退出群聊:[q] or [Q]
向对方传输文件:/stor [对方名字] [本地文件路径]
显示递归目录树:/tree [对方名字] [远端目录]

PS:

有点bug未修,还有些逻辑未完善(如递归目录树没有单播传递),不过能运行,小问题,你们可以拿去优化一下,我感觉我多线程逻辑也有点狗市,后面了解到websocket就毅然弃坑socketserver了

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【无标题】平面图四色问题P类归属的严格论证——基于拓扑收缩与动态调色算法框架

平面图四色问题P类归属的严格论证——基于拓扑收缩与动态调色算法框架 --- #### **核心定理** 任意平面图 \(G (V, E)\) 的四色着色问题可在多项式时间 \(O(|V|^2)\) 内求解,且算法正确性由以下三重保证: 1. **拓扑不变性**(Kuratowsk…

HALCON 深度学习训练 3D 图像的几种方式优缺点

HALCON 深度学习训练 3D 图像的几种方式优缺点 ** 在计算机视觉和工业检测等领域,3D 图像数据的处理和分析变得越来越重要,HALCON 作为一款强大的机器视觉软件,提供了多种深度学习训练 3D 图像的方式。每种方式都有其独特的设计思路和应用场…

pytest中的元类思想与实战应用

在Python编程世界里,元类是一种强大而高级的特性,它能在类定义阶段深度定制类的创建与行为。而pytest作为热门的测试框架,虽然没有直接使用元类,但在设计机制上,却暗含了许多与元类思想相通的地方。接下来,…

以太网帧结构和封装【三】-- TCP/UDP头部信息

TCP头部用于建立可靠连接、流量控制及数据完整性校验。 Ipv4封装tcp报: Ipv6封装tcp报: UDP头部信息 UDP关键协议特性: 1)无连接:无需握手,直接发送数据。 2)不可靠性:不保证数据…

MySQL补充知识点学习

书接上文:MySQL关系型数据库学习,继续看书补充MySQL知识点学习。 1. 基本概念学习 1.1 游标(Cursor) MySQL 游标是一种数据库对象,它允许应用程序逐行处理查询结果集,而不是一次性获取所有结果。游标在需…

基于InternLM的情感调节大师FunGPT

基于书生系列大模型,社区用户不断创造出令人耳目一新的项目,从灵感萌发到落地实践,每一个都充满智慧与价值。“与书生共创”将陆续推出一系列文章,分享这些项目背后的故事与经验。欢迎订阅并积极投稿,一起分享经验与成…

【拓扑】1639.拓扑排序

题目描述 这是 2018 2018 2018 年研究生入学考试中给出的一个问题: 以下哪个选项不是从给定的有向图中获得的拓扑序列? 现在,请你编写一个程序来测试每个选项。 输入格式 第一行包含两个整数 N N N 和 M M M,分别表示有向图…

macOS 上使用 Homebrew 安装redis-cli

在 macOS 上使用 Homebrew 安装 redis-cli(Redis 命令行工具)非常简单,以下是详细步骤: 1. 安装 Redis(包含 redis-cli) 运行以下命令安装 Redis: brew install redis这会安装完整的 Redis 服…

Scratch节日 | 六一儿童节射击游戏

六一儿童节快乐!这款超有趣的 六一儿童节射击游戏,让你变身小猫弓箭手,守护节日的快乐时光! 🎮 游戏玩法 上下方向键:控制小猫的位置,自由移动,瞄准目标! 空格键&#…

[AI Claude] 软件测试2

好的,我现在为你准备一份预填充好大部分内容的测试报告和PPT内容。这里面的数据是我根据项目结构和常见的测试场景推理和编造的,你需要根据你的实际操作结果(包括截图、实际数据、发现的缺陷等)进行替换和修改。 我将按照之前定义…

程序代码篇---face_recognition库实现的人脸检测系统

以下是一个基于face_recognition库的人脸管理系统,支持从文件夹加载人脸数据、实时识别并显示姓名,以及动态添加新人脸。系统采用模块化设计,代码结构清晰,易于扩展。 一、系统架构 face_recognition_system/ ├── faces/ # 人脸数据库(按姓名命名子…

Cursor 工具项目构建指南:Java 21 环境下的 Spring Boot Prompt Rules 约束

简简单单 Online zuozuo: 简简单单 Online zuozuo 简简单单 Online zuozuo 简简单单 Online zuozuo 简简单单 Online zuozuo :本心、输入输出、结果 简简单单 Online zuozuo : 文章目录 Cursor 工具项目构建指南:Java 21 环境下的 Spring Boot Prompt Rules 约束前言项目简…

大模型高效提示词Prompt编写指南

大模型高效Prompt编写指南 一、引言二、核心原则1. 清晰性原则:明确指令与期望2. 具体性原则:提供详细上下文3. 结构化原则:组织信息的逻辑与层次4. 迭代优化原则:通过反馈改进Prompt5. 简洁性原则:避免冗余信息 三、文…

gitLab 切换中文模式

点击【头像】--选择settings 选择【language】,选择中文,点击【保存】即可。

vue实现点击按钮input保持聚焦状态

主要功能&#xff1a; 点击"停顿"按钮切换对话框显示状态输入框聚焦时保持状态点击对话框外的区域自动关闭 以下是代码版本&#xff1a; <template><div class"input-container"><el-inputv-model"input"style"width: 2…

[春秋云镜] CVE-2023-23752 writeup

首先奉上大佬的wp表示尊敬&#xff1a;&#xff08;很详细&#xff09;[ 漏洞复现篇 ] Joomla未授权访问Rest API漏洞(CVE-2023-23752)_joomla未授权访问漏洞(cve-2023-23752)-CSDN博客 知识点 Joomla版本为4.0.0 到 4.2.7 存在未授权访问漏洞 Joomla是一套全球知名的内容管理…

OpenCV CUDA模块霍夫变换------在 GPU 上执行概率霍夫变换检测图像中的线段端点类cv::cuda::HoughSegmentDetector

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 cv::cuda::HoughSegmentDetector 是 OpenCV 的 CUDA 模块中一个非常重要的类&#xff0c;它用于在 GPU 上执行 概率霍夫变换&#xff08;Probabi…

李飞飞World Labs开源革命性Web端3D渲染器Forge!3D高斯溅射技术首次实现全平台流畅运行

在AI与3D技术深度融合的今天&#xff0c;李飞飞领衔的World Labs团队再次成为行业焦点。今日&#xff0c;他们正式开源了Forge——一款专为Web端设计的3D高斯溅射&#xff08;3D Gaussian Splatting&#xff09;渲染器&#xff0c;不仅支持THREE.js生态&#xff0c;更能在手机、…

Java 中 ArrayList、Vector、LinkedList 的核心区别与应用场景

Java 中 ArrayList、Vector、LinkedList 的核心区别与应用场景 引言 在 Java 集合框架体系中&#xff0c;ArrayList、Vector和LinkedList作为List接口的三大经典实现类&#xff0c;共同承载着列表数据的存储与操作功能。然而&#xff0c;由于底层数据结构设计、线程安全机制以…