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

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

课程:《Python程序设计》
班级: 2222
姓名: 李東霖
学号:20222223
实验教师:王志强
实验日期:2025年4月16日
必修/选修: 公选课

一、实验内容

(一)实验内容

创建服务端和客户端,服务端在特定端口监听多个客户请求。客户端和服务端通过Socket套接字(TCP/UDP)进行通信。

(二)实验要求

1.创建服务端和客户端,选择一个通信端口,用Python语言编程实现通信演示程序;

2.要求包含文件的基本操作,例如打开和读写操作。

3.要求发送方从文件读取内容,加密后并传输;接收方收到密文并解密,保存在文件中。

4.程序代码托管到码云。

注:在华为ECS服务器(OpenOuler系统)和物理机(Windows/Linux系统)上使用VIM、PDB、IDLE、Pycharm等工具编程实现。

二、实验过程及结果

(一)通信功能实现

1.客户端相关代码:
import socket

def show_menu():
    print("\n===== 网络通信系统菜单 =====")
    print("1. 基本通信功能")
    print("2. 加密文件传输功能")
    print("0. 退出")
    return input("请选择功能 (0-2): ")

def basic_communication(client):
    while True:
        message = input("请输入要发送的消息: ")
        if message == "exit":
            client.send(message.encode())
            print("通信中断")
            break
        client.send(message.encode())
        response = client.recv(1024).decode()
        print(f"服务器响应: {response}")



def main():
    # 建立连接
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    try:
        client.connect((HOST, PORT))

        while True:
            choice = show_menu()

            if choice == "0":
                print("退出系统")
                break

            client.send(choice.encode())

            if choice == "1":
                basic_communication(client)
            elif choice == "2":
                encrypted_transfer(client)
            else:
                print("无效选择,请重新输入")

    except ConnectionRefusedError:
        print("错误: 无法连接到服务器")
    except Exception as e:
        print(f"发生错误: {str(e)}")
    finally:
        client.close()  # 关闭连接


HOST = "192.168.0.103"
PORT = 5555
main()
客户端通信过程的逻辑如下:
客户端启动时创建Socket并连接到服务器
客户端显示菜单选项(1.基本通信,2.加密文件传输,0.退出)
用户选择1后,客户端将选择发送给服务器,随后进入消息循环
用户输入消息,发送至服务端(输入"exit"退出)
等待并显示服务器响应
通信完成(输入exit)关闭Socket连接
程序结束
2.服务器相关代码
import socket
import threading

def handle_client(client_socket, addr):
    print(f"[*] 接受来自 {addr[0]}:{addr[1]} 的连接")
    try:
        while True:  # 添加循环保持连接
            # 接收客户端选择的功能
            choice = client_socket.recv(1024).decode()
            if choice == "0":
                print(f"[*] 客户端 {addr} 请求断开连接")
                break
            if choice == "1":
                # 基本通信功能
                while True:
                    message = client_socket.recv(1024).decode()
                    if message == "exit":
                        break
                    print(f"[{addr}] 收到消息: {message}")
                    reply = input("请输入回复:")
                    client_socket.send(reply.encode())

def start_server(host, port):
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.bind((host, port))
    server.listen(5)
    print(f"[*] 服务器监听 {host}:{port}")

    try:
        while True:
            client_sock, addr = server.accept()
            client_handler = threading.Thread(
                target=handle_client,
                args=(client_sock, addr)
            )
            client_handler.start()
    except KeyboardInterrupt:
        print("\n[*] 服务器关闭")
    finally:
        server.close()

HOST = "192.168.0.103"
PORT = 5555
start_server(HOST, PORT)
服务器通信过程的逻辑如下:
服务器启动,创建Socket (socket.AF_INET, socket.SOCK_STREAM)
绑定到指定IP和端口 (HOST = "192.168.0.103", PORT = 5555)
开始监听,设置最大挂起连接数为5
当有新客户端连接时:server.accept()接受连接,返回客户端socket和地址
为每个客户端创建新线程处理 (threading.Thread)
接收客户端选择的功能("0"退出,"1"基本通信,"2"加密传输)
选择1基本通信功时,循环接收客户端消息(收到"exit"时退出循环)
打印收到的消息
等待输入回复内容
将回复发送给客户端
3.运行结果

客户端:
image
服务器:
image

(二)文件操作及加密传输

1.客户端代码
import socket

# 异或加密/解密函数
def xor_crypt(data, key):
    return bytes([b ^ key for b in data])

def show_menu():
    print("\n===== 网络通信系统菜单 =====")
    print("1. 基本通信功能")
    print("2. 加密文件传输功能")
    print("0. 退出")
    return input("请选择功能 (0-2): ")

def encrypted_transfer(client):
    try:
        # 获取异或加密密钥
        key = int(input("请输入异或加密密钥(0-255): "))
        if not 0 <= key <= 255:
            raise ValueError

        #传输密钥和文件名
        client.send(str(key).encode())
        filename = input("请输入要加密传输的文件: ")
        client.send(filename.encode())

        # 读取并加密文件
        with open(filename, "rb") as file:
            file_data = file.read()
        encrypted_data = xor_crypt(file_data, key)

        # 分块发送加密数据
        chunk_size = 4096
        for i in range(0, len(encrypted_data), chunk_size):
            client.send(encrypted_data[i:i + chunk_size])

        # 发送结束标记
        client.shutdown(socket.SHUT_WR)

        # 接收服务器响应
        response = client.recv(1024).decode()
        print(f"服务器响应: {response}")

    except ValueError:
        print("错误: 密钥必须是0-255之间的整数")
    except Exception as e:
        print(f"文件传输失败: {str(e)}")

def main():
    # 建立连接
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    try:
        client.connect((HOST, PORT))

        while True:
            choice = show_menu()

            if choice == "0":
                print("退出系统")
                break

            client.send(choice.encode())

            if choice == "1":
                basic_communication(client)
            elif choice == "2":
                encrypted_transfer(client)
            else:
                print("无效选择,请重新输入")

    except ConnectionRefusedError:
        print("错误: 无法连接到服务器")
    except Exception as e:
        print(f"发生错误: {str(e)}")
    finally:
        client.close()  # 关闭连接


HOST = "120.46.29.197"#云服务器的公网IP
PORT = 8080
main()
客户端逻辑:
客户端启动时创建Socket并连接到服务器
客户端显示菜单选项(1.基本通信,2.加密文件传输,0.退出)
选择2进入文件传输
用户输入加密密钥(0-255的整数,加密方式为异或)
客户端发送密钥给服务器
用户输入要传输的文件名
客户端发送文件名给服务器
客户端执行文件传输:
  读取文件内容
  使用异或加密算法加密文件数据
  分块(4096字节/块)发送加密数据
发送完成后关闭写入端(SHUT_WR)
接收并显示服务器响应
2.服务器代码:
import socket
import threading

# 异或加密/解密函数
def xor_crypt(data, key):
    return bytes([b ^ key for b in data])

def handle_client(client_socket, addr):
    print(f"[*] 接受来自 {addr[0]}:{addr[1]} 的连接")
    try:
        while True:  # 添加循环保持连接
            # 接收客户端选择的功能
            choice = client_socket.recv(1024).decode()
            if choice == "0":
                print(f"[*] 客户端 {addr} 请求断开连接")
                break
            if choice == "1":
                # 基本通信功能
                while True:
                    message = client_socket.recv(1024).decode()
                    if message == "exit":
                        break
                    print(f"[{addr}] 收到消息: {message}")
                    reply = input("请输入回复:")
                    client_socket.send(reply.encode())

            elif choice == "2":
                # 加密传输功能
                try:
                    # 接收密钥
                    key = int(client_socket.recv(1024).decode())
                    print(f"[*] 使用异或密钥: {key}")
                    # 接收文件名
                    filename = client_socket.recv(1024).decode()
                    print(f"[*] 接收文件: {filename}")
                    # 接收加密数据
                    encrypted_data = b""
                    while True:
                        chunk = client_socket.recv(4096)
                        if not chunk:
                            break
                        encrypted_data += chunk
                    print(f"收到密文:\n{encrypted_data}")
                    # 解密数据
                    decrypted_data = xor_crypt(encrypted_data, key)
                    print(f"解密结果为:\n{decrypted_data}")
                    # 保存解密后的文件
                    save_path = f"decrypted_{filename}"
                    with open(save_path, "wb") as file:
                        file.write(decrypted_data)
                    print(f"[+] 文件已解密保存到: {save_path}")
                    # 发送确认信息给客户端
                    client_socket.send(f"文件接收解密成功,保存为: {save_path}".encode())

                except Exception as e:
                    print(f"[!] 解密失败: {str(e)}")
                    client_socket.send(f"解密失败: {str(e)}".encode())

    except Exception as e:
        print(f"[!] 处理 {addr} 时出错: {str(e)}")
    finally:
        client_socket.close()


def start_server(host, port):
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.bind((host, port))
    server.listen(5)
    print(f"[*] 服务器监听 {host}:{port}")

    try:
        while True:
            client_sock, addr = server.accept()
            client_handler = threading.Thread(
                target=handle_client,
                args=(client_sock, addr)
            )
            client_handler.start()
    except KeyboardInterrupt:
        print("\n[*] 服务器关闭")
    finally:
        server.close()



HOST = "192.168.10.184"#云服务器的映射IP
PORT = 8080
start_server(HOST, PORT)

服务器逻辑:
服务器启动,创建Socket (socket.AF_INET, socket.SOCK_STREAM)
绑定到指定IP和端口 (HOST = "192.168.10.184", PORT = 5555)
开始监听,设置最大挂起连接数为5
当有新客户端连接时:server.accept()接受连接,返回客户端socket和地址
为每个客户端创建新线程处理 (threading.Thread)
接收客户端选择的功能("0"退出,"1"基本通信,"2"加密传输)
选择2进入加密传输
接收加密密钥(0-255的整数,用于异或)
接收文件名
分块接收加密文件数据(4096字节/块)
使用异或算法解密数据
将解密数据保存为"decrypted_原文件名"
发送操作结果给客户端
3.运行结果

客户端:
image
服务器:
image
image

文件内容:
客户端发送:
image
服务器接收:
image

(三)上传代码至码云

https://gitee.com/doronlee/python/tree/master/实验三
image

三、实验过程中遇到的问题和解决过程

  • 问题1:无法安装cryptography库,不能使用库内的加密函数

  • 问题1解决方案:没能解决安装库的问题,使用简单的异或加密代替

  • 问题2:文件传输出现乱码

  • 问题2解决方案:传输过程中明确编码方式可以避免乱码问题

# 发送方
with open('file.txt', 'r', encoding='utf-8') as f:
    content = f.read()
socket.send(content.encode('utf-8'))

# 接收方
data = socket.recv(1024)
content = data.decode('utf-8')

其他(感悟、思考等)

我了解过在linux中使用C语言进行socket编程,学会python后发现有许多的相似之处,都有相同的流程:创建Socket → 绑定地址 → 监听(服务端)/连接(客户端) → 接受连接(服务端) → 发送/接收数据 → 关闭连接。但是使用python更方便,不需要手动管理内存。

参考资料

posted @ 2025-04-22 21:54  doronlee  阅读(56)  评论(0)    收藏  举报