20242402 实验三《Python程序设计》实验报告

20242402 2024-2025-2 《Python程序设计》实验三报告

课程:《Python程序设计》
班级:2424
姓名:张宇涵
学号:20242402
实验教师:王志强
实验日期:2025年4月16日
必修/选修: 公选课

1.实验内容

(1)创建服务端和客户端,选择一个通信端口,用Python语言编程及通过socket套接字实现通信演示程序;

(2)包含文件的基本操作,例如打开和读写操作。

(3)发送方从文件读取内容,使用AES加密或XOR加密后并传输;接收方收到密文并解密,保存在文件中。

(4)程序代码托管到码云。

2. 实验过程及结果

1.先安装pycryptodome库(AES加密的相关库);

2.在老师上课提供的程序,deepseek的帮助和自己亲自动手的多方协作下,完成了客户端,服务端的代码编写,具体逻辑如下:

  • 若选择交流,则客户端和服务端一句一句进行互相交流,直至输入bye

  • 若选择传输文件,可以选择两种加密方式,异或加密或AES加密。输入文件地址,即可完成加密,传输,解密,存储。

    代码如下:

    • 服务端:
点击查看代码
# -*- coding: utf-8 -*-
# @Time : 2025/5/1 10:07
# @Author : weare
# @Email : 1021149940@qq.com
# @File : server.py
# @Project : python-experiment
# -*- what can I say -*-
# -*- coding: utf-8 -*-
# 文件名: FinalServer_v2.py
import socket
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad

# region 加密配置
XOR_KEY = b'secret_key'  # 必须与客户端一致
AES_KEY = b'16byte-long-key!'


def xor_crypt(data: bytes) -> bytes:
    """异或解密函数(与加密使用相同函数)"""
    return bytes([b ^ XOR_KEY[i % len(XOR_KEY)] for i, b in enumerate(data)])


def aes_decrypt(encrypted: bytes) -> bytes:
    """AES解密函数
    Args:
        encrypted: 包含iv的加密数据
    Returns:
        解密后的原始字节数据
    """
    iv = encrypted[:16]
    cipher = AES.new(AES_KEY, AES.MODE_CBC, iv)
    return unpad(cipher.decrypt(encrypted[16:]), AES.block_size)


# endregion

def main():
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  # 允许端口重用
    server.bind(('localhost', 9999))
    server.listen(5)
    print("服务器已启动,等待连接...")

    try:
        client, addr = server.accept()
        print(f"接收到来自 {addr} 的连接")

        mode = client.recv(1).decode()  # 获取工作模式

        if mode == "1":
            # region 聊天模式处理
            print("进入聊天模式")
            while True:
                data = client.recv(1024)
                if not data or data.decode().lower() == "bye":  # 退出条件
                    print("客户端终止对话")
                    break
                print(f"客户端说:{data.decode('utf-8')}")
                reply = input("回复:")
                client.send(reply.encode('utf-8'))
            # endregion

        else:
            # region 文件传输处理
            encrypt_type = client.recv(1).decode()  # 获取加密方式
            print(f"文件传输模式,加密方式:{'异或' if encrypt_type == '1' else 'AES'}")

            # 接收完整数据
            encrypted = b''
            while True:
                chunk = client.recv(4096)
                if not chunk: break
                encrypted += chunk

            # 解密处理
            if encrypt_type == "1":
                decrypted = xor_crypt(encrypted)
            else:
                decrypted = aes_decrypt(encrypted)

            # 保存文件
            with open('received_file', 'wb') as f:
                f.write(decrypted)
            print(f"文件接收成功({len(decrypted)}字节)")
            # endregion

    finally:
        client.close()
        server.close()


if __name__ == '__main__':
    main()
  • 客户端
点击查看代码
# -*- coding: utf-8 -*-
# @Time : 2025/5/1 10:17
# @Author : weare
# @Email : 1021149940@qq.com
# @File : client.py
# @Project : python-experiment
# -*- what can I say -*-

import socket
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
import os

# region 加密配置
XOR_KEY = b'secret_key'  # 异或加密密钥(需与服务端一致)
AES_KEY = b'16byte-long-key!'  # AES密钥(需16/24/32字节)


def xor_crypt(data: bytes) -> bytes:
    """异或加密/解密函数(对称算法)
    Args:
        data: 输入字节数据(明文或密文)
    Returns:
        处理后的字节数据
    """
    return bytes([b ^ XOR_KEY[i % len(XOR_KEY)] for i, b in enumerate(data)])


def aes_encrypt(data: bytes) -> bytes:
    """AES加密函数
    Args:
        data: 待加密的原始字节数据
    Returns:
        iv(16字节) + 加密后的数据
    """
    iv = os.urandom(16)
    cipher = AES.new(AES_KEY, AES.MODE_CBC, iv)
    return iv + cipher.encrypt(pad(data, AES.block_size))


# endregion

def main():
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    try:
        client.connect(('localhost', 9999))
        print("已连接服务器!请选择模式:\n1. 聊天模式\n2. 文件传输")

        # region 模式选择
        while True:  # 输入验证循环
            mode = input(">> ")
            if mode in ("1", "2"):
                client.send(mode.encode())  # 发送模式标识
                break
            print("请输入1或2")
        # endregion

        if mode == "1":
            # region 聊天模式
            print("进入聊天模式(输入bye结束)")
            while True:
                msg = input("你说:")
                client.send(msg.encode('utf-8'))

                if msg.lower() == "bye":  # 退出条件
                    print("对话结束")
                    break

                reply = client.recv(1024).decode('utf-8')
                print(f"服务器回复:{reply}")
            # endregion

        else:
            # region 文件传输模式
            # 选择加密方式
            while True:
                encrypt_type = input("请选择加密方式:\n1. 异或加密\n2. AES加密\n>> ")
                if encrypt_type in ("1", "2"):
                    client.send(encrypt_type.encode())  # 发送加密方式
                    break
                print("输入无效,请重新选择!")

            file_path = input("输入要传输的文件路径:")
            try:
                with open(file_path, 'rb') as f:
                    file_data = f.read()

                # 加密处理
                if encrypt_type == "1":
                    encrypted = xor_crypt(file_data)
                else:
                    encrypted = aes_encrypt(file_data)

                client.sendall(encrypted)
                print(f"文件发送完成({len(file_data)}字节)")
            except FileNotFoundError:
                print("错误:文件不存在")
            # endregion

    finally:
        client.close()


if __name__ == '__main__':
    main()
  • 对每个分支进行测试:

    • 对话:

  • AES加密:

  • XOR加密:

3.代码托管到gitee中。

3. 实验过程中遇到的问题和解决过程

  • 问题1:在安装库的时候,控制台上无法安装,提示Requirement already satisfied: pycryptodome in d:\python\python3.7\lib\site-packages (3.22.0)
  • 问题1解决方案:在查询了CSDN后,转变思路,使用pycharm中自带的库安装的方法。如下图:

  • 问题2:第一次运行的时候,客户端报错:Traceback (most recent call last):
    File "D:\pycharm\python-experiment\client.py", line 78, in
    main()
    ~~~~^^
    File "D:\pycharm\python-experiment\client.py", line 33, in main
    server.bind(('localhost', 9999))
    ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^
    OSError: [WinError 10048] 通常每个套接字地址(协议/网络地址/端口)只允许使用一次。接着我去询问了deepseek。

  • 问题2解决方案:

    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.bind(('localhost', 9999)) # ❌ 客户端错误地绑定了端口
    

    错误发生在客户端尝试绑定服务器端口(这是服务端的行为),导致端口冲突。客户端应该使用connect()而非bind()。 将客户端中的错误修正,程序就可以正常运行了。

其他(感悟、思考等)

AES加密

首先呢,我很好奇AES加密算法的实现形式,但又怕看不懂,于是我就询问了deepseek:

现在我想了解一下aes加密实现方式是怎样的,他的每一步是怎样的,背后的数学原理是啥?请详细讲讲,说人话。谢谢。

deepseek给我的回复是:

我将用做菜的过程来类比AES加密,让你直观理解这个"加密厨房"如何运作。我们以最常见的AES-128(密钥长度128位)为例。


一、准备工作:切配食材(数据预处理)

  1. 切块处理:把明文切成16字节的小块(128位),比如"HelloWorld123456"会被切成:

    | H | e | l | l |
    | o | W | o | r |
    | l | d | 1 | 2 |
    | 3 | 4 | 5 | 6 |
    
  2. 腌制食材(密钥扩展):用主密钥生成11把不同的"调料刀"(轮密钥)

    • 主密钥是原始调料(比如:盐)
    • 通过特殊配方(Rijndael密钥扩展算法),生成10轮调料组合+1把初始调料刀

二、烹饪流程(加密10轮)

每轮操作就像不同的烹饪手法,这里展示核心四道工序:

1. 撒盐(字节替换 - SubBytes)
  • 每个字节通过一个特殊配方表(S盒)替换
  • 类似把"西红柿"替换成"糖渍番茄"
  • 示例:A3 → 0x8A

S盒替换示意图

2. 翻炒(行移位 - ShiftRows)
  • 第一行不动
  • 第二行左移1格
  • 第三行左移2格
  • 第四行左移3格
原始:
| A | B | C | D |
| E | F | G | H |
| I | J | K | L |
| M | N | O | P |

移位后:
| A | B | C | D |
| F | G | H | E |
| K | L | I | J |
| P | M | N | O |
3. 搅拌(列混淆 - MixColumns)
  • 每列进行矩阵乘法(在伽罗瓦域GF(2^8)上)
  • 类似用打蛋器把蛋液搅拌均匀
  • 数学公式:新字节 = 2*旧字节 ⊕ 3*旧字节 ⊕ 旧字节 ⊕ 旧字节
    (这里的⊕是异或操作,*是特殊乘法)
4. 加调料(轮密钥加 - AddRoundKey)
  • 当前数据块与轮密钥做异或操作(XOR)
  • 类似最后撒一把秘制调料
# 示例:XOR操作
数据:   01000001 (A)
轮密钥: 00010001 
结果:   01010000 (P)

三、核心数学原理

  1. 有限域(Galois Field)

    • 使用GF(2^8)这个数学系统(256个元素的有限域)
    • 所有运算结果都不会超过1字节(0-255)
    • 例如:加法=异或,乘法=特殊位移运算
  2. 扩散与混淆

    • 扩散:改变1个比特会影响整个加密结果(像打翻调料瓶影响整锅汤)
    • 混淆:密钥与密文的关系复杂到无法追踪(像调料比例成为商业机密)
  3. 轮次设计

    • 10轮操作形成"雪崩效应"
    • 即使只改变1比特,经过多轮操作后差异会被放大到整个数据块

四、为什么AES安全?

  1. 密钥空间大:AES-128有2^128 ≈ 3.4×10^38种可能密钥

    • 对比:地球沙子总数约7.5×10^18
  2. 数学基础牢固

    • 基于有限域理论和线性代数
    • 没有已知的数学捷径可破解
  3. 抗攻击设计

    • 差分攻击需2^100次操作
    • 线性攻击需2^118次操作

五、完整流程图示

graph TD A[16字节明文] --> B(初始轮密钥加) B --> C{循环10次} C --> D[字节替换] D --> E[行移位] E --> F[列混淆] F --> G[轮密钥加] G --> C C --> H(最终轮) H --> I[字节替换] I --> J[行移位] J --> K[轮密钥加] K --> L[16字节密文]

六、现实类比

想象你在做一个10层的加密蛋糕:

  1. 每层都要:涂果酱(字节替换)→ 旋转蛋糕(行移位)→ 混合奶油(列混淆)→ 撒糖粉(轮密钥加)
  2. 最后一层不用混合奶油(最终轮没有MixColumns)
  3. 每层使用的糖粉配方不同(轮密钥不同)

这样的蛋糕即使你知道制作步骤,也无法逆向推出原始配方(密钥)。这就是AES的安全精髓!

它的解释很有意思啊,把加密类比成做饭,确实是实现了我提示词中的说人话,便于让我这个小白有了一个初步的印象。

选择操作的实现

选择哪种操作这部分我不太会,是deepseek帮我实现的,然后我让他解释了一下这部分:

while True:
 mode = input(">> ")
 if mode in ("1", "2"): # 输入有效性验证
 client.send(mode.encode()) # 发送模式标识
 break
 print("请输入1或2") # 错误提示
  1. 循环控制while True创建无限循环,确保必须得到有效输入

  2. 输入验证if mode in ("1", "2")检查输入是否为合法选项

  3. 协议同步client.send()将模式标识发送给服务端,建立通信约定

  4. 流程突破break退出循环继续后续操作

  5. 容错处理:错误提示帮助用户正确输入

参考资料

posted @ 2025-05-01 11:54  weare219  阅读(29)  评论(0)    收藏  举报