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地址,确实是个低级错误。

四、其他

    1. 像是AES加密算法这种需要自学,因为能力不足只能先学习借鉴别人的代码(参考文献《AES加密算法原理的详细介绍与实现》);
    1. 学会了查看ip地址、进程、端口等;
    1. 能够自己尝试实现跨设备通讯是很令人兴奋的,此前在C语言学习中没有接触过类似的部分。

五、参考资料

posted @ 2025-05-07 21:28  熵非时  阅读(24)  评论(0)    收藏  举报