20242215 实验三《Python程序设计》实验报告
20242215 2024-2025-2 《Python程序设计》实验三报告
课程:《Python程序设计》
班级: 2422
姓名: 贾瑞宁
学号:20242215
实验教师:王志强
实验日期:2025年4月16日
必修/选修: 公选课
一、实验内容
(一)基本内容
创建服务端和客户端,服务端在特定端口监听多个客户请求。客户端和服务端通过Socket套接字(TCP/UDP)进行通信。
(二)具体要求
(1)创建服务端和客户端,选择一个通信端口,用Python语言编程实现通信演示程序;
(2)要求包含文件的基本操作,例如打开和读写操作。
(3)要求发送方从文件读取内容,加密后并传输;接收方收到密文并解密,保存在文件中。
(4)程序代码托管到码云。
二、实验过程及结果
1. 创建基础的服务器和客户端,选择一个通信端口,用Python语言编程实现通信演示程序
- 启动服务端
![]()
- 服务端收到客户端连接
![]()
- 客户端发送信息
![]()
- 服务端收到信息
![]()
- 客户端收到服务端回复
![]()
- 尝试进行多轮对话
![]()
2. 尝试在不同设备间实现通讯
- 在cmd中输入
ipconfig查找服务端ip地址
![]()
- 在客户端和服务端代码对应位置输入服务端ip地址
- 分别测试客户端和服务端跨设备通讯功能
![]()
![]()
3. 改进客户端及服务端程序,加入文件传输及加密功能
阅读文献,我尝试使用 AES (Advanced Encryption Standard) 加密算法来加密数据。AES 是一种广泛使用的对称加密算法,能够有效地保护电子数据。
密钥和初始化向量
key = b'DKYERDKYERDKYERS': 用于加密和解密的密钥,长度为 16 字节(128 位),我随便打了一个。iv = b'dkyerdkyerdkyers': 初始化向量 (iv) 用于确保相同的明文在多次加密时产生不同的密文,我还是随便打了一个。
加密函数 aes_encrypt
cipher = AES.new(key, AES.MODE_CBC, iv): 创建一个新的 AES 加密器对象,使用 CBC (Cipher Block Chaining) 模式和我随便生成的密钥与 iv。padded_data = pad(data.encode('utf-8'), AES.block_size): 使用 PKCS7 填充算法对明文进行填充,使其长度是 AES 块大小(16 字节)的倍数。encrypted_data = cipher.encrypt(padded_data): 对填充后的数据进行加密,生成密文。
数据发送
-
client_socket.send(encrypted_data): 将加密后的数据通过套接字发送给服务器。 -
客户端加密发送新建文件、已有文件
![]()
数据接收
- 服务端接收两份文件,解密之,保存之
![]()
- 查看服务端保存的两份文件
![]()
服务端代码已上传至码云
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
import socket
import os
# 密钥和初始化向量,需要16字节
key = b'DKYERDKYERDKYERS'
iv = b'dkyerdkyerdkyers'
def aes_decrypt(encrypted_data):
try:
cipher = AES.new(key, AES.MODE_CBC, iv)
decrypted_data = cipher.decrypt(encrypted_data)
unpadded_data = unpad(decrypted_data, AES.block_size)
return unpadded_data.decode('utf-8')
except Exception as e:
print(f"解密过程出错: {e}")
return None
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = ('192.168.92.1', 8888)
log_file = 'server_log.txt'
client_socket = None
try:
server_socket.bind(server_address)
server_socket.listen(1)
print(f'服务器正在监听 {server_address[0]}:{server_address[1]}')
client_socket, client_address = server_socket.accept()
print(f'接受来自 {client_address} 的连接')
# 接收并解密客户端发送的初始数据
encrypted_data = client_socket.recv(4096)
decrypted_data = aes_decrypt(encrypted_data)
if decrypted_data is None:
raise ValueError("解密失败,无法继续执行。")
with open(log_file, 'w', encoding='utf-8') as log:
log.write("接收到的加密内容:\n")
log.write(encrypted_data.hex())
log.write("\n\n解密后的内容:\n")
log.write(decrypted_data)
script_dir = os.path.dirname(os.path.abspath(__file__))
file_path = os.path.join(script_dir, 'received_file.txt')
with open(file_path, 'w', encoding='utf-8') as file:
file.write(decrypted_data)
print('数据已解密并保存到 received_file.txt')
# 接收并解密客户端发送的已有本地文件内容
encrypted_existing_file_data = client_socket.recv(4096)
if encrypted_existing_file_data:
decrypted_existing_file_data = aes_decrypt(encrypted_existing_file_data)
if decrypted_existing_file_data is not None:
existing_file_path = os.path.join(script_dir, 'received_existing_file.txt')
with open(existing_file_path, 'w', encoding='utf-8') as file:
file.write(decrypted_existing_file_data)
print(f'已接收并保存来自客户端的已有文件内容到 {existing_file_path}')
else:
print("解密已有文件内容失败。")
except OSError as e:
print(f"服务器绑定或监听时出现错误: {e}")
except FileNotFoundError:
print("文件未找到,请检查文件路径。")
except Exception as e:
print(f"发生未知错误: {e}")
finally:
if client_socket:
client_socket.close()
if server_socket:
server_socket.close()
客户端代码已上传至码云
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
import os
import socket
# 密钥和初始化需和服务端保持一致
key = b'DKYERDKYERDKYERS'
iv = b'dkyerdkyerdkyers'
def aes_encrypt(data):
try:
cipher = AES.new(key, AES.MODE_CBC, iv)
padded_data = pad(data.encode('utf-8'), AES.block_size)
encrypted_data = cipher.encrypt(padded_data)
return encrypted_data
except Exception as e:
print(f"加密过程出错: {e}")
return None
server_address = ('192.168.92.1', 8888)
file_name = 'send_file.txt'
log_file = 'client_log.txt'
client_socket = None
try:
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect(server_address)
script_dir = os.path.dirname(os.path.abspath(__file__))
file_path = os.path.join(script_dir, file_name)
# 新建文件并输入内容
print("\n请输入要发送的内容(输入空行结束):")
content = []
while True:
line = input()
if not line:
break
content.append(line)
content = '\n'.join(content)
with open(file_path, 'w', encoding='utf-8') as file:
file.write(content)
print(f"内容已保存到 {file_name}")
with open(file_path, 'r', encoding='utf-8') as file:
plaintext_data = file.read()
encrypted_data = aes_encrypt(plaintext_data)
if encrypted_data is None:
raise ValueError("加密失败,无法继续执行。")
with open(log_file, 'w', encoding='utf-8') as log:
log.write("加密前的内容:\n")
log.write(plaintext_data)
log.write("\n\n加密后的内容:\n")
log.write(encrypted_data.hex())
confirm = input("\n是否发送文件内容?(Y/N): ").strip().upper()
if confirm == 'Y':
client_socket.send(encrypted_data)
print('数据已加密并发送')
else:
print("发送已取消。")
# 询问用户是否发送已有文件
send_existing_file = input("\n是否发送已有本地文件?(Y/N): ").strip().upper()
if send_existing_file == 'Y':
existing_file_path = input("请输入要发送的文件路径: ").strip()
if os.path.isfile(existing_file_path):
with open(existing_file_path, 'r', encoding='utf-8') as file:
existing_file_content = file.read()
existing_file_encrypted_data = aes_encrypt(existing_file_content)
if existing_file_encrypted_data is not None:
client_socket.send(existing_file_encrypted_data)
print(f"文件 {existing_file_path} 的内容已加密并发送。")
else:
print("加密已有文件内容失败。")
else:
print("文件路径无效或文件不存在。")
except ConnectionRefusedError:
print("无法连接到服务器,请检查服务端是否启动。")
except FileNotFoundError:
print("文件未找到,请检查文件路径。")
except Exception as e:
print(f"发生未知错误: {e}")
finally:
if client_socket:
client_socket.close()
三、实验过程中遇到的问题和解决过程
-
问题1:测试程序时,再次启动服务器程序,发现端口被占用(如下图)
![]()
-
问题1解决方案:询问AI,尝试在CMD中使用netstat命令查找占用端口的进程ID,然后在任务管理器中找到这个进程并结束之(如下图)
![]()
![]()
![]()
-
问题2:两台设备间无法连接(显示“积极拒绝”);
-
问题2解决方案:反复尝试,发现问题在于应该在服务端和客户端都填服务端的ip地址,确实是个低级错误。
四、其他
-
- 像是AES加密算法这种需要自学,因为能力不足只能先学习借鉴别人的代码(参考文献《AES加密算法原理的详细介绍与实现》);
-
- 学会了查看ip地址、进程、端口等;
-
- 能够自己尝试实现跨设备通讯是很令人兴奋的,此前在C语言学习中没有接触过类似的部分。















