20201324 2020-2021-2 《Python程序设计》实验三报告

20201324 2020-2021-2 《Python程序设计》实验三报告

课程:《Python程序设计》
班级: 2013
姓名: 徐源
学号:20201324
实验教师:王志强
实验日期:2020年5月27日
必修/选修: 公选课

一、实验内容

  1. 创建:创建服务端和客户端,服务端在特定端口监听多个客户请求,选择一个通信端口,用Python语言编程实现通信演示程序;

  2. 通信:客户端和服务端通过Socket套接字(TCP/UDP)进行通信,要求包含文件的基本操作,例如打开和读写操作;

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

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

二、实验过程及结果

实验思路

  1. 先创建服务端和客户端,实现一个服务端对应一个客户端的功能,再尝试一个服务端响应多个客户端;
  2. 构建循环,使通信可以进行多次;
  3. 分别针对服务端和客户端,编写代码,实现文件传输与接受;
  4. 运用AES,实现对文件的加密和解密;
  5. 整合上述功能,形成完整程序。

监听多个客户端

创建服务端

服务端

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 参数1:服务器之间网络通信;参数2:流式socket , for TCP
# s = socket.socket()#服务器端的socket初始化
s.bind(('127.0.0.1', 8001))#绑定. localhost = 127.0.0.1,port=0-65535
s.listen(5)#监听
socks = []  # 放每个客户端的socket

将监听和处理放到不同的线程进行处理;
将监听放入主线程,将处理放进子线程。

客户端

import socket
#客户端的Socket初始化
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)    #参数1:服务器之间网络通信;参数2:流式socket , for TCP
s.connect(('127.0.0.1', 8001))#连接,元组的形式,(IP地址,端口)

str = input("请输入要传输的内容:")
#s.sendall(str.encode())
data = s.recv(1024)
#print(data.decode())

# print(data)

s.close()

构建循环通信

必须放进一个while循环(如果不放进循环,监听一次就没了)

代码来源

def handle():
    while True:
        for k in socks:
            try:
                data = k.recv(1024)  #到这里程序继续向下执行
            except Exception as e:
                continue
            if not data:
                socks.remove(k)
                continue


t = threading.Thread(target=handle)  # 子线程
if __name__ == '__main__':
    t.start()
    print(r'我在%s线程中' % threading.current_thread().name)
    print('waiting for connecting ...')
    while True:
        clientSock, addr = s.accept()
        print('connected from:', addr)
        socks.append(clientSock)

运行结果

image

通过TCP进行通信

服务端

接收文件

def take_in(filename,s):
    while True:
        with open(filename, 'ab') as file:
            data = s.recv(1024)
            if data == b'quit':
                break
            file.write(data)
        s.sendall('done'.encode())
    print("File reception has been completed!")

发送文件

def send_out(filename,s):
    with open(filename, 'rb') as file:
        i = file.read()
        s.send(i)
    s.send('quit'.encode())
    print("File has been sent out successfully!")

客户端

发送文件

def send_out(filename):
    with open(filename, 'rb') as file:
        for i in file:
            CliSock.send(i)
            data = CliSock.recv(1024)
            if data != b'done':
                break
    time.sleep(1)
    CliSock.send('quit'.encode())
    print("File has been sent out successfully")

接收文件

def take_in(af_filename):
    while True:
        with open(af_filename, 'ab') as file:
            data = CliSock.recv(1024)
            if data == b'quit':
                break
            file.write(data)
        time.sleep(1)
        CliSock.sendall('done'.encode())
    print("File reception has been completed!")

对文件的加密和解密

image
我选择使用AES加密和解密,前期需先下载Crypto模块;

我下载时遇到了比较多的问题,后文详细描述,最后是通过在cmd和pycharm终端分别输入pip install pycrypto安装成功的

将str补足为16的倍数

代码来源

注意!!密钥长度必须为16、24或32位,分别对应AES-128、AES-192和AES-256

加密过程就是用pyCryptodome模块带的aes先将秘钥,以及要加密的文本填充为16位,随后对aes产生的字节码进行base64位编码,转为字符串的形式即可,解密思想逆过来即可。先逆向解密base64成bytes,执行解密密并转码返回str,将多余位数的’\0’替换为空

def add_to_16(value):# str不是16的倍数那就补足为16的倍数
    while len(value) % 16 != 0:
        value += '\0'
    return str.encode(value)  # 返回bytes

服务端加密

def encrypt(filename,af_filename):#加密方法
    key = input("Please set a key:")
    text = open(filename,'rb').read()
    open(filename,'rb').close()
    text = str(text)
    aes = AES.new(add_to_16(key), AES.MODE_ECB)
    encrypt_aes = aes.encrypt(add_to_16(text))
    encrypted_text = str(base64.encodebytes(encrypt_aes), encoding='utf-8') # 执行加密并转码返回bytes
    print('The encrypted contents are:',encrypted_text)
    logbat = open(af_filename, 'w')
    logbat.write(encrypted_text)
    logbat.close()
    print('File has been encrypted successfully!')

客户端解密

def decrypt(af_filename):
    key = input('\nPlease enter the corresponding key:')
    text = str(open(af_filename, 'r').read())  # 密文文件
    open(af_filename, 'r').close()
    aes = AES.new(add_to_16(key), AES.MODE_ECB)  # 初始化加密器
    base64_decrypted = base64.decodebytes(text.encode(encoding='utf-8'))  # 优先逆向解密base64成bytes
    decrypted_text = str(aes.decrypt(base64_decrypted), encoding='utf-8').replace('\0', '')  # 执行解密
    decrypted_text1 =decrypted_text.decode('utf-8')
    print("\nFile has been decrypted successfully! the contents of the file are:")
    print(decrypted_text1)

运行结果

服务端

image

客户端

image

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

  • 问题1:下载Crypto模块时,我在cmd输入pip install pycrypto,显示下载成功,但pycharm报错:ModuleNotFoundError: No module named 'Crypto'

  • 问题1 分析:早先在c盘和d盘都下过python,可能下载了Crypto的python与pycharm所连接的并不是同一个。

  • 问题1解决方法:在pycharm终端也输入一次pip install pycrypto,并查看python39文件,确定里面有Crypto文件。但又报错:“ModuleNotFoundError:No module named ‘Crypto.Cipher’”,这时应先卸载pycrypto,再pip install pycryptodome,即可image

  • 问题2:解密时报错
    image
    这段代码在运行时会报错:UnicodeDecodeError: ‘utf-8’ codec can’t decode byte 0xa1 in position 0: invalid start byte

  • 问题2 分析:最初怀疑是用于加密的txt并不是用‘utf-8’保存的,或含有非法字符,但经检验可以排查这种假设
    image

    后经查找资料,认为可能是解码对参数的控制过于严格,我们不能改变errors参数的默认值,但可以覆盖默认错误=“strict”的处理程序
    image

    但这样修改后,程序运行后会出现无法识别的字符

  • 问题1解决方法:加一个eval()就可以了
    image

四、其他(感悟、思考等)

   算是做的最艰难的一个实验了,因为之前下载过很多个python的锅,实验开始没多久,pycharm就崩了,pycharm小白查方法查了很久才修好。虽然实验思路出来得很快,但面对网络上各种各样的代码,整合的过程对我来说难度很大。尤其是接连报错的时候,是换一份代码来copy还是给现有的代码缝缝补补是个很值得考虑的问题。爆肝几个晚上搞好,最后往码云push的时候,电脑十分适时地坏了,本地D盘中一切与"python"有关的东西统统丢掉了。。本实验被迫重做,写了四分之三的实验报告被迫重写,找好的资料被迫重找。。
   感觉做大的项目,自信心和意志力甚至是比敲代码能力更重要的东西。在艰难的时候不放弃自己,遇到无法挽回的局面时有随时重新来过的勇气,这样才能走的更远吧。

五、参考资料

posted @ 2021-05-29 21:38  20201324徐源  阅读(139)  评论(0编辑  收藏  举报