實驗目的
- 設計安全的信息傳輸工具,解決網絡傳輸涉密文件過程中的安全性問題。安全的信息傳輸,涉及多個密碼學知識點,在實驗設計過程中,不斷加深理解密碼學基本概念和算法基礎原理,並且能夠鍛煉獨立的代碼編寫能力和知識的綜合運用能力。
實驗儀器或設備
- 硬件:計算機1台
- 操作系統:Windows 10
- 軟件/平台:Visual Studio Code、QT
- 編程語言:Python
實驗內容
設計與實現一款局域網中點到點(即一台計算機到另外一台計算機)的信息傳輸工具,要求能夠保證信息在傳輸過程中的保密性、完整性和發送/接收方的不可否認性,以防止通信線路上的竊聽、泄露、破壞、篡改等不良操作。
- 采用對稱與非對稱混合加密方法,每進行一次通信,更改一次會話密鑰(用於信息加密的對稱密鑰)。
- 能夠實現對任意類型的文件進行安全傳輸。
- 可采用適當的開發語言和開發工具。
- 軟件用戶界面友好,提示信息完整,操作舒適。
- 提交實驗報告、包括分析和設計文檔及代碼清單。
- 平台不限。linux/windows/android
實驗步驟
設計思路+設計文檔
開發語言、開發平台、開發工具模塊的選擇
該實驗要求為設計完成一款文件安全傳輸軟件,前端要求界面友好操作舒適,功能要求實現通信、加密、傳輸。綜合考慮,選擇使用Python作為開發語言。因為Python自身帶有許多常用工具模塊,且用於加密解密的安全庫、用於通信傳輸的socket庫也齊全,因此非常方便用於開發軟件,結合使用可以節約開發時間,減少代碼量。本人常用於編寫Python的IDE為Visual Studio Code,因此本實驗也通過VS Code完成。因為使用Python編程,所以軟件界面設計選擇使用Python庫PyQt5,不過需要另外安裝。PyQt是一個創建GUI應用程序的工具包。它是Python編程語言和Qt庫的成功融合。Qt庫是目前最強大的庫之一。
總結如下:
- 通信、傳輸:socket庫
- DES加密解密:pyDes庫或Crypto庫
- RSA加密解密:rsa庫
- MD5哈希:md5庫(可選,因為rsa庫本身也有簽名+摘要+驗簽的功能)
- 軟件界面:PyQt5庫(包含界面設計、多線程等)
- 常用庫:random(生成隨機數)、os(文件操作)、re(正則表達式)、json(編解碼)、sys(系統操作)、threading(多線程)、time(時間)等
開發順序與大致框架
主要順序是先前端再后端。設計好前端界面后,可以對應前端界面的按鍵、內容顯示等部署后端功能,方便操作;相反,如果先完成后端功能,再設計前端界面,有可能出現前后端功能不匹配的問題,或是要重復修改后端功能,無疑增加了工作量。
前端部分,即軟件界面設計。根據實驗對於軟件的功能要求,需要設計出與用戶交互的按鍵、輸入框,以及內容顯示欄、提示框等。具體包括:發送鍵、接收鍵、取消鍵、IP地址輸入框、端口選擇框、文件選擇欄、接收文件信息反饋欄、狀態提示框、頁面切換鍵、進度條等。
后端部分,即功能實現,可根據實驗提供的“文件安全傳輸流程”圖完成。實現順序:
- 啟動前端界面,主線程。包含實現按鍵函數、讀取輸入信息、顯示提示信息。
- 服務端(監聽+發送方)與客戶端(請求+接收方)的通信,分為服務端一個新的通信線程和客戶端一個新的通信線程。包含通信連接、生成密鑰、密鑰交換的操作。
- 服務端與客戶端的信息傳輸,分為服務端一個新的發送線程和客戶端一個新的接收線程。包含加/解密文件、生成摘要、簽名、打包文件、發送/接收的操作。
自定義要素和功能
- 將“單文件安全傳輸”優化為了“多文件安全傳輸”。
- 發送文件前首先發送文件頭,其中包含文件名和文件大小等屬性。
- 文件接收后反饋文件接收狀態后再繼續發送下一文件,以確保是否需要重傳。
實驗分析(分析為什么這樣設計、安排)
“單文件安全傳輸”=>“多文件安全傳輸”
實驗中沒有明確要求是單文件傳輸或是多文件傳輸,考慮到軟件設計要人性化,我才設定為“多文件安全傳輸”,可以一次性選擇多文件進行安全發送。
主線程和分線程
主線程運行軟件窗口界面,而分線程分為了4個:服務端連接線程、客戶端連接線程、服務端發送線程、客戶端接收線程,分別負責實現不同的功能。
之所以要多線程運行,是由於等待連接和等待傳輸的過程如果放在主線程中,會造成界面假死的狀態,因此為了避免這種情況發生,采用多線程方式來運行通信連接和文件傳輸的步驟。
文件頭傳輸
發送文件之前會先發送文件頭,文件頭中包含文件名和文件大小以及當前傳輸狀態(正常、完成、出錯)。
文件頭是提前告知接收方的信息。文件名用於接收文件的命名;文件大小讓接收方獲知需要接收多少的數據;當前傳輸狀態讓接收方獲知多文件傳輸是否傳輸完成。
文件接收狀態反饋
接收方在接收完文件后需要對文件進行數據完整性的檢驗(即對比摘要內容),當比對結果出現錯誤,即表示當前接收的文件可能被篡改,當前通信並不安全,需要重新生成密鑰並交換,建立新的安全通信連接。
由於我將“單文件安全傳輸”優化為了“多文件安全傳輸”,因此發送方不能夠一次將全部文件都發送給接收方,而應該等待接收方對當前文件作出反饋,表示該文件數據完整性是否良好,再進一步發送下一個文件,否則重新建立連接並重傳該文件。
實驗過程與結果
實驗過程
界面設計
界面設計使用Qt平台,Qt平台中提供有圖形化界面進行界面設計,十分方便。如下圖為“發送文件”界面的設計圖。
如下圖為“接收文件”界面的設計圖。
另存為文件“file.ui”。
界面(前端)代碼生成
Python中提供pyuic5工具,可以將file.ui的界面布局文件轉換成Python代碼,免去了繁瑣的敲代碼生成界面的過程。命令為“pyuic5 -o file.py file.ui”表示源文件為file.ui生成目標文件file.py。請注意,此時生成的代碼file.py僅僅實現了界面顯示,並未實現軟件后端的功能。
編寫(后端)功能代碼
前端代碼生成后,根據前端界面的控件名稱,在后端代碼實現通信傳輸功能的同時控制前端界面顯示相應內容。
程序打包
代碼編寫后的文件為.py文件,可以使用Python工具PyInstaller,將代碼以及相關依賴庫打包成.exe程序文件。命令為“pyinstaller -F -i favicon.ico run.py -w”,-F表示打包為單文件程序,-i表示綁定程序圖標為favicon.ico,run.py為主程序代碼,-w表示程序運行時不顯示命令行窗口。具體過程如下:
實驗代碼清單+關鍵代碼分析
代碼清單
- run.py:主程序代碼,負責生成軟件主窗口,以及各種按鍵函數。還包含四個分線程類(服務器連接、客戶端連接、服務器發送、客戶端接收)。
- sendfile.py:發送操作類,包含所有的發送文件操作。
- recfile.py:接收操作類,包含所有的接收文件操作
- des.py:DES加解密操作。
- rsa1.py:RSA加解密操作。
- md5.py(可選):MD5哈希操作。
- utility.py:其他工具函數,如生成目錄、生成隨機數、判斷輸入內容是否為IP地址。
- file.py:file.ui生成的界面代碼,由run.py運行啟動。
- 注1:代碼文件中有詳細的代碼注釋,可以直接查看代碼文件進行閱讀。
關鍵代碼分析
- sendfile.py的Sender類的sendPro函數
- 函數首先判斷當前是否是重傳文件的操作,若是,則恢復到之前的狀態。
- for循環遍歷需要上傳的文件。
- 使用DES生成DES密鑰的同時,加密當前文件。
- packupFile函數中包含RSA加密DES密鑰、MD5哈希生成文件摘要、打包文件的三個操作。
- 根據打包后的文件,生成相應的文件頭,文件頭包含文件名稱、文件大小。
- 發送文件頭大小,再發送文件頭,接着發送打包文件。
- 發送完打包文件后,修改發送界面的進度條顯示進度。
- 每發送完一個文件,在while循環中等待接收方對接收的文件作出反饋。接收的反饋內容與文件頭類似,需要先獲取長度再獲取內容並且解碼。如果反饋正常,則繼續下一步,否則直接返回錯誤False和當前錯誤文件的索引,准備重新建立連接並重傳文件。
- for循環的最后,判斷是否發送結束,若是,則發送“完成”的文件頭告知接收方;否則繼續循環發送文件。
# 發送過程
def sendPro(self, fileIndex):
if fileIndex != -1: # 重傳操作
self.sendFileCount = fileIndex
# 遍歷文件列表
for index in range(self.sendFileCount, len(self.files)):
# 判斷是否按了取消鍵
if not self.mainWin.sendState:
return False, -1
file = self.files[index]
de = DEncry() # 創建DES加密對象
des_filename = de.des_encrypt_file(file) # DES加密文件
# md5_filename = md5_encrypt_file(file) # 生成文件摘要
filename = self.packupFile(
file, des_filename) # 打包DES密鑰、加密文件、簽名摘要,參數:原文件、DES加密文件
head_info_len, head_info = self.operafile(filename, file) # 生成文件頭
self.conn.send(head_info_len) # 發送文件頭長度,4個字節
self.conn.send(head_info.encode('utf-8')) # 文件頭編碼,發送文件頭的內容
self.sendRealFile(filename, self.filesize_bytes) # 發送打包文件
# 設置進度條顯示
progress = (index+1) / len(self.files) * 100
self.mainWin.totalpro_progressBar.setValue(progress)
# 等待接收端回復文件狀態
while True:
send_file_state_len = self.conn.recv(4) # 接收發送狀態的長度
if len(send_file_state_len) == 4:
send_file_state_len = struct.unpack(
'i', send_file_state_len)[0]
# 接收指定長度的發送狀態
send_file_state = self.conn.recv(send_file_state_len)
send_file_state = json.loads(
send_file_state.decode('utf-8')) # 發送狀態解碼
# 斷言當前文件索引與發送狀態中的文件索引一致
assert send_file_state['fileIndex'] == index
# 失敗,返回False和對應文件索引
if not send_file_state['state']:
return False, send_file_state['fileIndex']
break
# 發送完畢
if index == len(self.files) - 1:
head_info_len, head_info = self.operafile() # 生成“發送結束”的文件頭
self.conn.send(head_info_len) # 發送文件頭長度,4個字節
self.conn.send(head_info.encode('utf-8')) # 文件頭編碼,發送文件頭的內容
return True, -1 # 成功發送所有文件則返回true和文件索引為-1
- recfile.py的Recver類的recvPro函數
- 函數首先判斷當前是否是重傳文件的操作,若是,則恢復到之前的狀態。
- 第一個while循環不斷等待接收文件,期間會拋出timeout異常。
- 第二個while循環則不斷接收文件,並在開頭判斷是否按下了“取消”的按鍵,若是則停止接收文件。
- 接着,接收文件頭長度,並根據長度接收文件頭,獲取文件名、文件大小和發送狀態。
- 若發送狀態為“transiting”,則表示還有文件需要接收,則進行接收操作,由recv_file函數負責完成,其中包含解密、驗簽、比對摘要等操作。接收后,還需要返回主界面相關的顯示信息,並且反饋給發送端當前文件狀態是否正常是否需要重傳。
- 若發送狀態為“final”,則返回主界面相關的顯示信息,並結束接收文件。
- 若發送狀態為“error”,則馬上停止接收,並返回出錯信息。
- 兩個except用於處理拋出的異常。一個是rsa.VerificationError,即比對摘要時出現不一致,會反饋給發送端需要重傳,並且返回False和當前錯誤文件索引給客戶端表示需要重新建立連接。另一個是BaseExcption,包含“等待接收文件”的超時異常,進行相關提示信息的顯示,表示對方還未發送文件。
# 接收文件過程
def RecvPro(self, fileIndex):
if fileIndex != -1: # 重傳操作
self.recFileCount = fileIndex
while True:
try:
self.conn.settimeout(5)
# self.mainWin.request_Button.setEnabled(False) # 開始監聽按鈕無效化
self.mainWin.cancel_Button2.setEnabled(True) # 取消按鈕有效化
while True:
# 判斷是否按了取消鍵
if self.mainWin.recvState == False:
# self.request_Button.setEnabled(True) # 開始監聽按鈕有效化
break
struct_len = self.conn.recv(4) # 接收文件頭的長度
if len(struct_len) == 4:
self.mainWin.state_label2.setText("狀態:正在接收文件...")
head_dir = self.getFileHeader(
self.conn, struct_len) # 接收文件頭
if head_dir['filestate'] == 'transiting': # 文件頭屬性為接收狀態
# # 文件信息
# filename = head_dir['filename']
# filesize = head_dir['filesize_bytes']
lineEdit = '正在接收第' + str(self.recFileCount + 1) + \
'號文件【' + head_dir['filename'] + '】......'
self.recFileList(lineEdit) # 顯示正在接收信息
self.recv_file(head_dir, self.conn) # 開始接收文件
self.recvFileState(True) # 發送文件接收狀態:成功
self.recFileCount += 1 # 接收文件數加1
lineEdit = '成功接收第' + str(self.recFileCount) + \
'號文件【' + head_dir['filename'] + '】 --> ' + \
os.getcwd() + '\\' + head_dir['filename']
self.recFileList(lineEdit) # 顯示接收信息
elif head_dir['filestate'] == 'final': # 文件頭屬性為完成接收狀態
self.mainWin.recvState = False
lineEdit = '已完成文件接收!'
self.recFileList(lineEdit) # 顯示正在接收信息
self.mainWin.state_label2.setText("狀態:完成文件接收")
break
else: # 文件頭屬性為錯誤狀態
self.mainWin.recvState = False
lineEdit = '接收文件過程中發生錯誤!'
self.recFileList(lineEdit) # 顯示正在接收信息
self.mainWin.state_label2.setText("狀態:發生未知錯誤")
break
except rsa.VerificationError: # rsa驗簽拋出錯誤
self.recvFileState(False) # 發送文件接收狀態:失敗/錯誤
lineEdit = '對比接收文件摘要不符,驗簽失敗!准備重傳文件...'
self.recFileList(lineEdit) # 顯示正在接收信息
self.mainWin.state_label2.setText("狀態:准備重連...")
return False, self.recFileCount # 返回false和當前已接收文件數
except BaseException: # 如果建立連接后,該連接在設定的時間內無數據發來,則time out
self.mainWin.state_label2.setText("狀態:已連接,等待文件...")
if self.mainWin.recvState == False:
break
return True, -1 # 成功接收所有文件則返回true和文件索引為-1
軟件演示
功能介紹
- 發送界面展示
- 接收界面展示
- 多文件選擇
- 安全通信連接:生成密鑰、交換密鑰
- 文件安全傳輸:加密文件、解密文件、文件摘要比對
- 文件摘要比對失敗后重連並重傳文件
功能驗證
-
注意事項:打包后的軟件,暫時無法在Windows XP、Windows 2003以下的版本中運行,只能在Windows 7和Windows 10中運行。
-
驗證環境:兩台局域網主機。
-
安全通信連接:生成密鑰、交換密鑰
打開軟件所在文件夾,可以看到有一個文件夾icon和一個可執行文件run.exe。其中icon文件夾中包含程序圖標以及圖片,需要與run.exe放置在同一文件夾下方可使用。
雙擊啟動run.exe程序,就開始運行該軟件了。可以看到文件夾中多了一個新的文件夾“.tempfile”,這個文件夾用於存放通信傳輸使用的公私密鑰、對稱密鑰以及產生的臨時文件,此時該文件夾仍為空。
服務端(監聽方/發送方)主機IP為172.27.24.67,客戶端(請求方/接收方)主機IP為172.27.24.68。在發送端處運行發送界面,輸入發送端IP 172.27.24.67,端口8080,點擊“監聽端口”,便開始監聽本機8080端口。當接收端也發出了請求,則出現如下界面,表示成功連接並已生成RSA公私密鑰對和交換RSA公鑰。
同理,在接收端處運行接收界面。同樣,輸入發送端IP 172.27.24.67,端口8080,點擊“准備接收”,就會向發送端主機的8080端口發出請求,稍后便會顯示“已連接”。表示連接已建立並交換了密鑰。
查看發送端的臨時文件夾.tempfile,可以看到有3個RSA密鑰文件,分別是:對方(即接收端)公鑰、自己的私鑰、自己的公鑰。
查看接收端的臨時文件夾.tempfile,可以看到有3個RSA密鑰文件,分別是:對方(即發送端)公鑰、自己的私鑰、自己的公鑰。
上述步驟說明了該軟件完成了通信連接、RSA密鑰生成以及密鑰交換的功能。
-
文件安全傳輸:加密文件、解密文件、文件摘要比對
首先在發送端處,添加文件,這里為演示方便,僅添加了兩個小型文件.txt和.png,添加完成后會在下方的文件列表中顯示對應的文件路徑以及文件大小,點擊發送即可。
注1:由於DES加密解密的原因,選擇幾十MB以上的文件進行傳輸會導致運行速度很慢,看上去像“程序假死”,但其實軟件一直在運行,只是還在加密解密文件的過程中。我嘗試過優化/更換DES加密解密算法,但效果不佳。為演示方便,在此使用文件大小較小的文件做演示。
注2:此處還能使用“刪除”鍵和“清空”鍵,“刪除”鍵需要選擇文件列表中某一行后才可刪除,“清空”鍵可直接點擊則會一次性刪除所有文件。
發送完成后,發送端界面如下所示。文件列表清空,進度條顯示100%,提示框提示:發送完成。
接收端界面如下所示。內容顯示框中顯示了對應接收文件的存放路徑,進度條顯示100%,提示框提示:接收完成。
根據顯示的存放路徑找到接收的文件,即存放在接收端的軟件運行的當前文件夾中。為了以示區別,我在接收的文件都加上了前綴名“new”,以與原文件進行區分。
再次查看發送端的.tempfile文件夾,可以發現新增了4個文件,分別是1.txt經過DES加密后的臨時文件(密文)、titleicon.png經過DES加密后的臨時文件(密文)、DES密鑰文件(明文)、打包准備發送的文件(密文)。
查看接收端的.tempfile文件夾,可以發現新增了2個文件,分別是經過RSA解密后的DES密鑰文件(明文)、接收的原始文件(密文)。
-
文件摘要比對失敗后重連並重傳文件
注:由於實際演示中,較難模擬黑客攻擊篡改了文件,因此我從源代碼處進行修改,將原本摘要比對正確的情況,修改為摘要比對錯誤的情況。
功能描述:當出現摘要比對錯誤的情況,接收端會反饋給發送端,此時接收端和發送端會立即終止當前連接,重新生成密鑰、重新建立連接、重新交換密鑰,最后重傳該文件。重新建立連接的過程中,會更換與上一次連接不同的端口。
演示中,使用的是127.0.0.1的本機IP,即自己跟自己進行安全通信傳輸。接收端中不斷顯示“摘要比對錯誤”,因此與發送端不斷進行重連(包括重新產生RSA公私密鑰對和重新交換公鑰)和重傳文件,期間雙方的通信端口不斷作加一更換,從8080到8088。
- 演示策略
為方便演示該軟件的功能,可在一台主機上啟動該軟件,默認使用發送界面和接收界面的IP:127.0.0.1,實際效果便是主機自己與自己進行通信傳輸。
然后便可正常使用連接、文件傳輸功能了,只需來回切換“發送文件”界面和“接收文件”界面即可。
實驗總結
實驗心得
通過本次實驗,加深了對於網絡傳輸涉密文件過程中的安全性問題的理解,掌握了DES加/解密、RSA加/解密、MD5哈希等密碼學概念和算法原理,熟悉了文件安全傳輸的整體流程。實踐中,大量的代碼量編寫鍛煉了我對於程序調試的綜合能力,從軟件設計中新涉獵到了QT這樣的設計平台,對於軟件工程這門學科也有了新的理解。收獲甚多,仍需努力。
參考資料
- 將如下代碼拷入空.html文件,形成書簽文件,從瀏覽器“書簽管理器”導入即可。分類不足,僅供參考,包含關鍵技術以及踩坑解決方案。
<!DOCTYPE NETSCAPE-Bookmark-file-1>
<!-- This is an automatically generated file.
It will be read and overwritten.
DO NOT EDIT! -->
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
<TITLE>Bookmarks</TITLE>
<H1>Bookmarks</H1>
<DL><p>
<DT><H3 ADD_DATE="1585267644" LAST_MODIFIED="1604585852" PERSONAL_TOOLBAR_FOLDER="true">書簽欄</H3>
<DL><p>
<DT><H3 ADD_DATE="1605107979" LAST_MODIFIED="1605107979">文件安全傳輸軟件實現參考資料</H3>
<DL><p>
<DT><A HREF="https://blog.csdn.net/chen_yi_ang/article/details/87284323" ADD_DATE="1605107979">信息的安全傳輸實驗_kynehc-CSDN博客</A>
<DT><H3 ADD_DATE="1605107979" LAST_MODIFIED="1605107979">界面編程</H3>
<DL><p>
<DT><A HREF="https://blog.csdn.net/tTU1EvLDeLFq5btqiK/article/details/78693348?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param" ADD_DATE="1605107979">七個Python圖形應用GUI開發框架</A>
<DT><A HREF="https://www.zhihu.com/question/19555504" ADD_DATE="1605107979">Python跨平台圖形界面編程庫推薦</A>
<DT><A HREF="https://blog.csdn.net/m0_37329910/article/details/86557942" ADD_DATE="1605107979">python PyQt5 教程_zhangji-CSDN博客</A>
<DT><A HREF="https://blog.csdn.net/weixin_41929524/article/details/81456308" ADD_DATE="1605107979">Python制作小軟件——1. 安裝並使用PyQt5進行界面設計_Kanny-CSDN博客</A>
<DT><A HREF="https://blog.csdn.net/weixin_41929524/article/details/81460203?utm_medium=distribute.pc_relevant.none-task-blog-title-4&spm=1001.2101.3001.4242" ADD_DATE="1605107979">Python制作小軟件——2. 實現界面中的退出功能_Kanny-CSDN博客</A>
<DT><A HREF="https://blog.csdn.net/weixin_41929524/article/details/81475935?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.channel_param" ADD_DATE="1605107979">Python制作小軟件——3. 利用PyQt5實現界面中的功能_Kanny-CSDN博客</A>
<DT><A HREF="https://blog.csdn.net/weixin_42061064/article/details/108051962?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param" ADD_DATE="1605107979">解決PyQt designer啟動運行問題</A>
<DT><A HREF="https://www.cnblogs.com/codeofmine/p/12637880.html" ADD_DATE="1605107979">vscode+PyQt+QtDesigner配置</A>
<DT><A HREF="https://zhidao.baidu.com/question/2051943530923041467.html" ADD_DATE="1605107979">把兩個界面通過信號槽連接</A>
<DT><A HREF="https://www.cnblogs.com/luoxiang/p/13452889.html" ADD_DATE="1605107979">文件傳輸界面參考</A>
<DT><A HREF="https://www.jb51.net/article/167015.htm" ADD_DATE="1605107979">Qt Designer工具的使用案例</A>
<DT><A HREF="http://c.biancheng.net/view/1820.html" ADD_DATE="1605107979">Qt項目界面文件(.ui)及其作用</A>
<DT><A HREF="https://blog.csdn.net/yogima/article/details/74012112#1%E5%8F%AF%E4%BD%BF%E7%94%A8%E6%8F%90%E4%BE%9B%E7%9A%84%E6%A7%BD%E5%87%BD%E6%95%B0clear%E6%88%96lower" ADD_DATE="1605107979">QtDesigner控件+槽函數</A>
<DT><A HREF="https://blog.csdn.net/amwha/article/details/85873921" ADD_DATE="1605107979">QPushButton互斥選擇</A>
<DT><A HREF="https://blog.csdn.net/humanking7/article/details/80546728" ADD_DATE="1605107979">文件選擇</A>
<DT><A HREF="https://www.cnblogs.com/liming19680104/p/10373966.html" ADD_DATE="1605107979">文件列表顯示QListWidget</A>
<DT><A HREF="https://blog.csdn.net/weixin_38403778/article/details/82464181" ADD_DATE="1605107979">QTableWidget清空或刪除內容功能1</A>
<DT><A HREF="https://www.cnblogs.com/felix-wang/p/6210184.html" ADD_DATE="1605107979">QTableWidget行選中/刪除/添加行2</A>
<DT><A HREF="https://www.cnblogs.com/ygzhaof/p/10057722.html" ADD_DATE="1605107979">獲取/設置QLabel/lineEdit內容</A>
<DT><A HREF="https://blog.csdn.net/weixin_43496130/article/details/104242307" ADD_DATE="1605107979">獲取QSpinBox值</A>
<DT><A HREF="https://www.cnblogs.com/ccz320/p/6536993.html" ADD_DATE="1605107979">判斷是否是IP</A>
<DT><A HREF="https://blog.csdn.net/wbdxz/article/details/86290139" ADD_DATE="1605107979">PyQt5+socket編程界面卡住未響應</A>
<DT><A HREF="https://www.cnblogs.com/chenfulin5/p/10307754.html" ADD_DATE="1605107979">threading多參數傳遞方法</A>
<DT><A HREF="https://payloads.online/archivers/2019-04-02/2" ADD_DATE="1605107979">BaseRequestHandler 傳參問題</A>
<DT><A HREF="https://www.cnblogs.com/lutt/p/12115755.html" ADD_DATE="1605107979">python-os模塊拆分文件路徑和文件名</A>
<DT><A HREF="https://blog.csdn.net/Dontla/article/details/103823134" ADD_DATE="1605107979">struct解包時報錯 struct.error: unpack requires a buffer of 4 bytes</A>
<DT><A HREF="https://blog.csdn.net/qq_32446743/article/details/80309836" ADD_DATE="1605107979">斷開多線程(TcpSocketServer連接)</A>
<DT><A HREF="https://www.cnblogs.com/zwgbk/p/10268398.html" ADD_DATE="1605107979">pyqt5在textBrowser添加文本並自動滑動到底</A>
<DT><A HREF="https://www.jb51.net/article/132057.htm" ADD_DATE="1605107979">實現socket信息發送與監聽</A>
<DT><A HREF="https://blog.csdn.net/Quanworld/article/details/96101051?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.channel_param" ADD_DATE="1605107979">socket先后建立連接/等待連接問題</A>
<DT><A HREF="https://www.cnblogs.com/guohu/p/11320008.html" ADD_DATE="1605107979">Python創建目錄文件夾 - itprobie-菜鳥程序員 - 博客園</A>
<DT><A HREF="https://blog.csdn.net/txh3093/article/details/108562805" ADD_DATE="1605107979">基於pyqt5 構建彈窗進度條,在大型計算中實時顯示進度_txh3093的博客-CSDN博客</A>
</DL><p>
<DT><H3 ADD_DATE="1605107979" LAST_MODIFIED="1605107979">python局域網文件傳輸</H3>
<DL><p>
<DT><A HREF="https://www.cnblogs.com/xiaokang01/p/9069048.html" ADD_DATE="1605107979">python socket 傳輸文件 - Lucky& - 博客園</A>
<DT><A HREF="https://www.jb51.net/article/144628.htm" ADD_DATE="1605107979">python使用tcp實現局域網內文件傳輸_python_腳本之家</A>
</DL><p>
<DT><H3 ADD_DATE="1605107979" LAST_MODIFIED="1605107979">DES加密</H3>
<DL><p>
<DT><A HREF="http://www.jiuaitu.com/python/421.html" ADD_DATE="1605107979">python pyDes加密解密-凌的博客</A>
<DT><A HREF="https://www.cnblogs.com/Erma/p/12350416.html" ADD_DATE="1605107979">DES(python) - 程序媛墨禾 - 博客園</A>
<DT><A HREF="https://www.cnblogs.com/lvpzs/p/des.html" ADD_DATE="1605107979">Python使用DES加密解密 - LivPzs - 博客園</A>
<DT><A HREF="https://www.cnblogs.com/lanston1/p/11025881.html" ADD_DATE="1605107979">python中的md5加密 - lanston - 博客園</A>
<DT><A HREF="https://blog.csdn.net/sinat_25449961/article/details/78039656" ADD_DATE="1605107979">使用python生成rsa密鑰對_code_lab-CSDN博客</A>
<DT><A HREF="https://blog.csdn.net/whatday/article/details/97617461/" ADD_DATE="1605107979">python3 RSA加解密_whatday的專欄-CSDN博客</A>
<DT><A HREF="https://blog.csdn.net/l_d_56/article/details/84799457" ADD_DATE="1605107979">(1條消息) python隨機生成包含字母數字的六位驗證碼_l_d_56的博客-CSDN博客</A>
<DT><A HREF="https://blog.csdn.net/One_of_them/article/details/82049232" ADD_DATE="1605107979">python中cryptodome的DES_One of them的博客-CSDN博客</A>
<DT><A HREF="https://blog.csdn.net/weixin_30758821/article/details/98288238" ADD_DATE="1605107979">Python DES 加密解密,就是大家所謂想要的那個非常快速的方法_weixin_30758821的博客-CSDN博客</A>
</DL><p>
<DT><H3 ADD_DATE="1605107979" LAST_MODIFIED="1605107979">多線程</H3>
<DL><p>
<DT><A HREF="https://blog.csdn.net/bfz_50/article/details/93391672" ADD_DATE="1605107979">(1條消息) 【Python】socket同時收發與多線程防止input阻塞_bfz_50的博客-CSDN博客</A>
<DT><A HREF="https://blog.csdn.net/xyisv/article/details/88292870?utm_medium=distribute.pc_aggpage_search_result.none-task-blog-2~all~first_rank_v2~rank_v25-4-88292870.nonecase&utm_term=pyqt%20%E9%98%BB%E5%A1%9E%E7%95%8C%E9%9D%A2" ADD_DATE="1605107979">(1條消息) PyQt5多線程刷新界面防假死_徐奕的專欄-CSDN博客</A>
<DT><A HREF="https://www.cnblogs.com/XJT2018/p/10222981.html" ADD_DATE="1605107979">【PyQt5-Qt Designer】pyqtSignal()-高級自定義信號與槽 - XJT2019 - 博客園</A>
<DT><A HREF="https://blog.csdn.net/noricky/article/details/80048561" ADD_DATE="1605107979">(1條消息) QThread的用法:開啟與退出_Like a lunatic-CSDN博客</A>
<DT><A HREF="https://www.cnpython.com/qa/78282" ADD_DATE="1605107979">如何從QThread和Queu運行的函數返回值 - 問答 - Python中文網</A>
<DT><A HREF="https://www.imsonggg.com/archives/121" ADD_DATE="1605107979">python3 關於 Socket 關閉后 地址被重復使用的問題解決 | 舊城以西</A>
<DT><A HREF="https://www.cnblogs.com/xilouch/p/4618903.html" ADD_DATE="1605107979">python socket 編程之三:長連接、短連接以及心跳 - 葯師Aric - 博客園</A>
</DL><p>
<DT><H3 ADD_DATE="1605107979" LAST_MODIFIED="1605107979">界面美化</H3>
<DL><p>
<DT><A HREF="https://blog.csdn.net/Nirvana_6174/article/details/88427071" ADD_DATE="1605107979">pyqt5美化界面</A>
<DT><A HREF="https://blog.csdn.net/qq_38463737/article/details/107955083?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param" ADD_DATE="1605107979">PyQt5 按鈕QSS美化集_集電極-CSDN博客</A>
<DT><A HREF="https://www.cnblogs.com/newstart/p/4370203.html" ADD_DATE="1605107979">Qt中漂亮的幾款QSS - 夜&楓 - 博客園</A>
<DT><A HREF="https://blog.csdn.net/yechaoa/article/details/89092914" ADD_DATE="1605107979">css background-image 背景圖片自適應寬高_yechaoa-CSDN博客</A>
<DT><A HREF="https://blog.csdn.net/bochen_cwx/article/details/82083570" ADD_DATE="1605107979">QT實現背景圖片多種填充方式:居中、平鋪、縮放、拉伸_bochen_cwx的博客-CSDN博客</A>
<DT><A HREF="https://blog.csdn.net/qq_25939803/article/details/100066444" ADD_DATE="1605107979">Python-PyQt5-背景圖</A>
</DL><p>
<DT><A HREF="https://blog.csdn.net/zjkpy_5/article/details/89971191" ADD_DATE="1605107979">ModuleNotFoundError: No module named 'Crypto'_木下瞳的博客-CSDN博客</A>
<DT><A HREF="https://www.it610.com/article/1292321683318841344.htm" ADD_DATE="1605107979">9. PKI - 三種密鑰交換算法詳解(RSA& DHE& ECDHE)及他們在SSL/TLS協議中的應用 - it610.com</A>
</DL><p>
</DL><p>
</DL><p>