20243408 2025-2026-2 《Python程序设计》实验3报告

20243408 2025-2026-2 《Python程序设计》实验3报告

课程 :《Python程序设计》
班级 : 2434
姓名 : 梁灏
学号 :20243408
实验教师 :王志强
实验日期 :2026年4月28日
必修/选修: 公选课

1. 实验内容

本次实验围绕Socket网络编程技术展开,要求如下:

  • 创建服务端和客户端: 服务端在特定端口监听多个客户请求。客户端和服务端通过Socket套接字(TCP/UDP)进行通信。
  • 实现加密传输以及优化: 发送方输入内容,加密后并传输;接收方收到密文并解密和显示。同时实现代码优化。
    此外,实验还涉及代码版本管理(托管至码云)以及在华为OpenEuler系统上的部署验证。

2. 实验过程及结果

2.1 创建服务端和用户端

2.1.1 代码验证

基于老师提供的SocketServer.pySocketClient.py基础代码,进行了如下操作:

  1. 通信基础: 沿用文档中的TCP Socket通信模型,服务端绑定IP与端口并监听,客户端发起连接。
  2. 更改IP地址: 将两台计算机连接至同一网络下,分别修改IP地址为:192.168.43.8192.168.43.29。如图2-1、图2-2所示。

图2-1 计算机1(192.168.43.8)
图2-2 计算机2(192.168.43.29)

  1. 队友互测: 我与队友20243429付家祺分别运行服务端和客户端程序,成功进行了加密通信测试。
    如图2-3所示。

图2-3 两台计算机互相传送信息

2.1.2 关键代码与功能分析

SocketClient.pySocketServer.py分别实现了服务端和客户端的功能。

  1. 启动与连接

    • 服务端 先行启动,在 localhost(本机)的 4444 端口上"守候"(listen)。
    • 客户端 随后启动,主动向服务端的地址和端口发起连接(connect)。
    • 连接建立后,双方进入对话循环。
  2. 对话循环

    这是代码最核心的特点,双方收发消息的顺序是固定且交替的:

    • 服务端循环接收 -> (处理/显示) -> 等待用户输入 -> 发送
    • 客户端循环等待用户输入 -> 发送 -> 接收 -> (处理/显示)

    具体到一次完整的消息往返:

    1. 客户端 必须首先在控制台输入一句话,然后发送。
    2. 服务端 接收到这句话并显示,然后才能在控制台输入回复,发送回去。
    3. 客户端 收到回复并显示,之后才能输入下一条消息。
      如此循环往复。任何一方试图"抢话"(在不该自己发言的轮次输入)都会导致程序在 recv()input() 处阻塞等待。
  3. 退出机制

    • 任何一方在输入时键入 "exit" 并发送,都会在发送完该消息后终止自己的循环。
    • 接收方收到 "exit" 消息或空数据时,也会终止循环。
    • 双方关闭连接(close())。
  4. 网络连接建立

    • 服务端

      python 复制代码
      server.bind((HOST, PORT))  # 绑定地址端口,如同设定电话号码
      server.listen(1)            # 开始监听,允许1个连接排队
      conn, addr = server.accept() # 等待并接受连接,conn是专属通话管道
    • 客户端

      python 复制代码
      client.connect((HOST, PORT)) # 主动拨打服务端的"电话号码"
  5. 数据的收发与编解码

    • 发送conn.send(send_msg.encode("utf-8"))
      将字符串(str)使用UTF-8编码转换为字节序列(bytes)后通过网络发送。
    • 接收data = conn.recv(1024).decode("utf-8")
      从网络接收字节数据(最多1024字节),然后使用UTF-8解码为字符串。
  6. 同步控制

    • 控制逻辑由双方代码中 sendrecv固定顺序 天然实现,形成了上述的"锁步"效果。input()recv() 都是阻塞调用,强制了这种顺序。
  7. 程序特点与局限性

  • 优点

    • 结构清晰:非常直观地展示了TCP Socket编程的基本步骤(创建、绑定/连接、收发、关闭)。
    • 逻辑简单:适合初学者理解网络通信的基础流程。
  • 局限性

    • 严格同步:聊天体验不自然,双方不能自由地随时发送消息。
    • 单线程阻塞:在等待输入或接收消息时,程序无法做任何其他事情。
    • 仅支持单客户端 :服务端的 accept() 只调用一次,处理完一个客户端的整个会话后才会结束,期间无法服务其他客户端。
    • 脆弱的数据边界 :使用recv(1024),如果单条消息长度超过1024字节会被截断,如果小于1024字节则可能一次收到多条消息的片段,导致解码或逻辑混乱。这在当前简单字符串聊天中可能不显现,但是一个潜在问题。
    • 数据明文传输:未采用任何形式加密,安全性存疑。

2.1.3 程序运行与结果分析

  1. 运行过程

    • 首先在主机A运行SocketServer.py启动服务端。
    • 随后在主机B运行SocketClient.py,连接至主机A的IP和端口4444。
    • 双方交替输入文字,程序自动完成发送、接收的过程。
  2. 运行结果
    (以localhost形式测试)

    • 服务端控制台输出示例

      复制代码
      D:\Python_Class\Python_Class\.venv\Scripts\python.exe
      D:\Python_Class\socket\SocketServer.py 
      服务端已经启动,等待客户端连接......
      已连接客户端:('127.0.0.1', 10683)
      客户端:From Client
      我:From Server
      ......
    • 客户端控制台输出示例

      复制代码
      D:\Python_Class\Python_Class\.venv\Scripts\python.exe
      D:\Python_Class\socket\SocketClient.py 
      已连接服务端!输入exit退出聊天!
      我:From CLient
      服务端:From Server
      我:
      ......
  3. 结果分析

    • 成功实现了通信基本功能,且经实操验证。(见2.1.1

2.2 实现加密传输以及优化

2.2.1 基于LLM生成带有加密传输功能的代码

  1. 生成优化后代码

    针对2.1.2提及的局限性,向Deepseek输入提示词:

    复制代码
    目前存在以下问题,请解决:
    1、严格同步:聊天体验不自然,双方不能自由地随时发送消息。
    2、单线程阻塞:在等待输入或接收消息时,程序无法做任何其他事情。
    3、仅支持单客户端:服务端的 accept()只调用一次,处理完一个客户端的整个会话后才会结束,期间无法服务其他客户端。
    4、脆弱的数据边界:使用recv(1024),如果单条消息长度超过1024字节会被截断,如果小于1024字节则可能一次收到多条消息的片段,导致解码或逻辑混乱。这在当前简单字符串聊天中可能不显现,但是一个潜在问题。
    5、数据明文传输:未采用任何形式加密。
    帮我完成问题解决后,提供给我SocketClient_enhanced.py、SocketServer_enhanced.py。

    Deepseek生成代码如下:
    SocketServer_enhanced.py

    python 复制代码
    # -*- coding: utf-8 -*-
    import socket
    import struct
    import threading
    from queue import Queue
    import hashlib
    import os
    from Crypto.Cipher import AES
    from Crypto.Util.Padding import pad, unpad
    from Crypto.Random import get_random_bytes
    import base64
     class AESCipher:
        """AES加密解密类"""
    
        def __init__(self, key=None):
            if key is None:
                key = get_random_bytes(32)  # 256位密钥
            self.key = hashlib.sha256(key).digest()[:32]  # 确保32字节
    
        def encrypt(self, plaintext):
            """加密明文"""
            iv = get_random_bytes(16)  # 随机生成IV
            cipher = AES.new(self.key, AES.MODE_CBC, iv)
            ciphertext = cipher.encrypt(pad(plaintext.encode('utf-8'), AES.block_size))
            return iv + ciphertext
    
        def decrypt(self, ciphertext_with_iv):
            """解密密文"""
            iv = ciphertext_with_iv[:16]
            ciphertext = ciphertext_with_iv[16:]
            cipher = AES.new(self.key, AES.MODE_CBC, iv)
            plaintext = unpad(cipher.decrypt(ciphertext), AES.block_size)
            return plaintext.decode('utf-8')
    
    
    def send_encrypted_message(sock, aes_cipher, message):
        """发送加密消息"""
        try:
            # 加密消息
            encrypted_data = aes_cipher.encrypt(message)
            # 发送加密后的数据长度和内容
            header = struct.pack('!I', len(encrypted_data))
            sock.sendall(header + encrypted_data)
            return True
        except (BrokenPipeError, ConnectionResetError, OSError):
            return False
    
    
    def recv_encrypted_message(sock, aes_cipher):
        """接收并解密消息"""
        try:
            # 读取消息长度
            header = sock.recv(4)
            if not header:
                return None, None
            msg_len = struct.unpack('!I', header)[0]
    
            # 读取加密消息
            data = b''
            while len(data) < msg_len:
                packet = sock.recv(min(msg_len - len(data), 4096))
                if not packet:
                    return None, None
                data += packet
    
            # 解密消息
            plaintext = aes_cipher.decrypt(data)
            return plaintext, data
        except (ConnectionResetError, struct.error, OSError, ValueError):
            return None, None
    
    
    def handle_client(conn, addr, client_id, shared_key):
        """处理单个客户端连接"""
        print(f"[服务器] 客户端 #{client_id} {addr} 已连接")
        print(f"[服务器] 与客户端 #{client_id} 的共享密钥: {shared_key.hex()[:16]}...")
    
        # 创建AES加密器
        aes_cipher = AESCipher(shared_key)
    
        # 创建接收消息的线程
        recv_thread = threading.Thread(target=receive_from_client, args=(conn, addr, client_id, aes_cipher))
        recv_thread.daemon = True
        recv_thread.start()
    
        # 主线程用于发送消息
        try:
            while True:
                # 获取用户输入
                plaintext = input(f"[向客户端#{client_id}发送] ")
                if plaintext.lower() == 'exit':
                    print(f"[服务器] 终止与客户端 #{client_id} 的会话")
                    break
    
                # 加密并发送消息
                if send_encrypted_message(conn, aes_cipher, plaintext):
                    # 显示明文和密文
                    encrypted_data = aes_cipher.encrypt(plaintext)
                    ciphertext_hex = base64.b64encode(encrypted_data[16:]).decode()  # 不包含IV
                    print(f"  [明文] {plaintext}")
                    print(f"  [密文] {ciphertext_hex[:64]}...")
                else:
                    print(f"[服务器] 客户端 #{client_id} 连接已断开")
                    break
        except (EOFError, KeyboardInterrupt):
            pass
        finally:
            conn.close()
            print(f"[服务器] 与客户端 #{client_id} 的连接已关闭")
    
    
    def receive_from_client(conn, addr, client_id, aes_cipher):
        """接收来自客户端的消息"""
        while True:
            plaintext, ciphertext_with_iv = recv_encrypted_message(conn, aes_cipher)
    
            if plaintext is None:
                print(f"[服务器] 客户端 #{client_id} 断开连接")
                break
    
            # 显示接收到的密文和明文
            ciphertext_hex = base64.b64encode(ciphertext_with_iv[16:]).decode()  # 不包含IV
            print(f"\n[客户端#{client_id} 说]")
            print(f"  [密文] {ciphertext_hex[:64]}...")
            print(f"  [明文] {plaintext}")
    
    
    def main():
        server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        HOST = "localhost"
        PORT = 4444
    
        server.bind((HOST, PORT))
        server.listen(5)
        print(f"[服务器] 启动在 {HOST}:{PORT},等待客户端连接...")
        print("[服务器] 生成共享密钥中...")
    
        client_counter = 0
    
        try:
            while True:
                conn, addr = server.accept()
                client_counter += 1
    
                # 生成并发送共享密钥
                shared_key = get_random_bytes(32)
                conn.sendall(shared_key)
    
                # 为每个客户端创建处理线程
                client_thread = threading.Thread(target=handle_client, args=(conn, addr, client_counter, shared_key))
                client_thread.daemon = True
                client_thread.start()
        except KeyboardInterrupt:
            print("\n[服务器] 正在关闭...")
        finally:
            server.close()
    
    
    if __name__ == "__main__":
        main()

    SocketClient_enhanced.py

    python 复制代码
    # -*- coding: utf-8 -*-
    import socket
    import struct
    import threading
    import sys
    import hashlib
    from Crypto.Cipher import AES
    from Crypto.Util.Padding import pad, unpad
    from Crypto.Random import get_random_bytes
    import base64
    
    
    class AESCipher:
        """AES加密解密类"""
    
        def __init__(self, key=None):
            if key is None:
                key = get_random_bytes(32)  # 256位密钥
            self.key = hashlib.sha256(key).digest()[:32]  # 确保32字节
    
        def encrypt(self, plaintext):
            """加密明文"""
            iv = get_random_bytes(16)  # 随机生成IV
            cipher = AES.new(self.key, AES.MODE_CBC, iv)
            ciphertext = cipher.encrypt(pad(plaintext.encode('utf-8'), AES.block_size))
            return iv + ciphertext
    
        def decrypt(self, ciphertext_with_iv):
            """解密密文"""
            iv = ciphertext_with_iv[:16]
            ciphertext = ciphertext_with_iv[16:]
            cipher = AES.new(self.key, AES.MODE_CBC, iv)
            plaintext = unpad(cipher.decrypt(ciphertext), AES.block_size)
            return plaintext.decode('utf-8')
    
    
    def send_encrypted_message(sock, aes_cipher, message):
        """发送加密消息"""
        try:
            # 加密消息
            encrypted_data = aes_cipher.encrypt(message)
            # 发送加密后的数据长度和内容
            header = struct.pack('!I', len(encrypted_data))
            sock.sendall(header + encrypted_data)
            return True
        except (BrokenPipeError, ConnectionResetError, OSError):
            return False
    
    
    def recv_encrypted_message(sock, aes_cipher):
        """接收并解密消息"""
        try:
            # 读取消息长度
            header = sock.recv(4)
            if not header:
                return None, None
            msg_len = struct.unpack('!I', header)[0]
    
            # 读取加密消息
            data = b''
            while len(data) < msg_len:
                packet = sock.recv(min(msg_len - len(data), 4096))
                if not packet:
                    return None, None
                data += packet
    
            # 解密消息
            plaintext = aes_cipher.decrypt(data)
            return plaintext, data
        except (ConnectionResetError, struct.error, OSError, ValueError):
            return None, None
    
    
    def receive_thread_func(sock, aes_cipher):
        """接收服务器消息的线程"""
        while True:
            plaintext, ciphertext_with_iv = recv_encrypted_message(sock, aes_cipher)
    
            if plaintext is None:
                print("\n[连接] 与服务器的连接已断开")
                sys.exit(0)
    
            # 显示接收到的密文和明文
            ciphertext_hex = base64.b64encode(ciphertext_with_iv[16:]).decode()  # 不包含IV
            print(f"\n[服务器]")
            print(f"  [密文] {ciphertext_hex[:64]}...")
            print(f"  [明文] {plaintext}")
            sys.stdout.write("我: ")
            sys.stdout.flush()
    
    
    def main():
        client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        HOST = "localhost"
        PORT = 4444
    
        try:
            client.connect((HOST, PORT))
        except ConnectionRefusedError:
            print("无法连接到服务器。请确保服务器已启动。")
            return
    
        print("已连接至服务器。")
    
        # 接收共享密钥
        shared_key = client.recv(32)
        print(f"从服务器获取共享密钥: {shared_key.hex()[:16]}...")
    
        # 创建AES加密器
        aes_cipher = AESCipher(shared_key)
    
        print("加密通道已建立。您可以随时输入消息,输入 'exit' 退出。\n")
    
        # 启动接收消息的线程
        recv_thread = threading.Thread(target=receive_thread_func, args=(client, aes_cipher))
        recv_thread.daemon = True
        recv_thread.start()
    
        # 主线程用于发送消息
        try:
            while True:
                # 获取用户输入
                plaintext = input("我: ")
                if plaintext.lower() == 'exit':
                    print("正在退出...")
                    break
    
                # 加密并发送消息
                if send_encrypted_message(client, aes_cipher, plaintext):
                    # 显示明文和密文
                    encrypted_data = aes_cipher.encrypt(plaintext)
                    ciphertext_hex = base64.b64encode(encrypted_data[16:]).decode()  # 不包含IV
                    print(f"  [明文] {plaintext}")
                    print(f"  [密文] {ciphertext_hex[:64]}...")
                else:
                    print("发送失败,连接可能已断开")
                    break
        except (EOFError, KeyboardInterrupt):
            print("\n正在退出...")
        finally:
            client.close()
    
    
    if __name__ == "__main__":
        # 检查依赖库
        try:
            from Crypto.Cipher import AES
        except ImportError:
            print("需要安装pycryptodome库,请运行: pip install pycryptodome")
            sys.exit(1)
    
        main()
  2. 运行测试

    • 首先在主机A运行SocketServer_enhanced.py启动服务端。
    • 随后在主机B运行SocketClient_enhanced.py,连接至主机A的IP和端口4444。
    • 双方交替输入文字,程序自动完成发送、接收的过程。
  3. 运行结果
    (以localhost形式测试)

    • 服务端控制台输出示例

      复制代码
      D:\Python_Class\Python_Class\.venv\Scripts\python.exe
      D:\Python_Class\socket\SocketServer_enhanced.py 
      [服务器] 启动在 localhost:4444,等待客户端连接...
      [服务器] 生成共享密钥中...
      [服务器] 客户端 #1 ('127.0.0.1', 4354) 已连接
      [服务器] 与客户端 #1 的共享密钥: 4003e30900aae59c...
      [向客户端#1发送] 
      [客户端#1 说]
        [密文] HTOnuncWcPvvIGyiyKdGMQ==...
        [明文] From Client
      
        [明文] 
        [密文] m4OxyLG8My5YH1hVVPzd7w==...
      [向客户端#1发送] From Server
        [明文] From Server
        [密文] hjcLJucmzxTT621rrycfrA==...
      [向客户端#1发送] 
        ......
    • 客户端控制台输出示例

      复制代码
      D:\Python_Class\Python_Class\.venv\Scripts\python.exe
      D:\Python_Class\socket\SocketClient_enhanced.py 
      已连接至服务器。
      从服务器获取共享密钥: 4003e30900aae59c...
      加密通道已建立。您可以随时输入消息,输入 'exit' 退出。
      
      我: From Client
        [明文] From Client
        [密文] qcIt0K/CLeSOVB7WFPoyTA==...
      我: 
      [服务器]
        [密文] +mpGQE84OzoQKBelQIuAQA==...
        [明文] 
      我: 
      [服务器]
        [密文] +pYq0gnwx8a/0NW06SkQyQ==...
        [明文] From Server
      我: 
        ......
  4. 结果分析

    • 成功实现了通信基本功能,且经实操验证。(如图2-4、图2-5所示)

图2-4 Server
图2-5 Client

2.2.2 基于LLM生成带有GUI的代码

  • 运行结果:如图2-6所示。

图2-5 带有图形界面的版本

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

  • 问题1 :两台计算机无法链接
    • 解决方案:链接至手机热点内,有可能是因为教室有人学号相同,使用了同一个IP地址。
  • 问题2 :Pycharm无法推送Git
    • 解决方案:重新安装。

其他(感悟、思考等)

  1. 理论与实践结合:本次实验让我对TCP Socket编程的"三次握手"、"流式传输"等概念有了更直观的认识。特别是处理消息边界问题,是理论学习中容易忽略的实践关键点。
  2. 安全无小事:实现加密功能让我体会到,即使是一个简单的聊天程序,要实现基本的数据保密性也需要仔细选择算法、处理密钥。
  3. AI辅助开发的利与弊 :LLM能极大提升原型开发速度,尤其是在生成GUI布局、模板代码方面。但其生成的代码可能不够健壮(如未处理多线程、异常),或不符合特定需求。因此,理解其生成的代码并具备调试、优化和整合的能力,比单纯会"提问"更为重要。
  4. 工具链的重要性:本次实验综合运用了Git(码云)、PyCharm等多种工具,让我感受到熟练掌握开发、调试、部署、版本管理的全流程工具,对高效完成项目至关重要。

参考资料

  1. Python官方文档 - socket 模块
  2. LLM (ChatGPT/DeepSeek) 在GUI代码生成和问题调试过程中的对话记录。
  • 代码仓库 :本实验所有代码(基础版、增强版、GUI版)均已托管至码云(Gitee),仓库地址:https://gitee.com/lucaslianghao/PythonClass