基于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
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/83683.shtml
英文地址,請注明出處:http://en.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;由于底層數據結構設計、線程安全機制以…