20201324 2020-2021-2 《Python程序设计》实验三报告
课程:《Python程序设计》
班级: 2013
姓名: 徐源
学号:20201324
实验教师:王志强
实验日期:2020年5月27日
必修/选修: 公选课
一、实验内容
-
创建:创建服务端和客户端,服务端在特定端口监听多个客户请求,选择一个通信端口,用Python语言编程实现通信演示程序;
-
通信:客户端和服务端通过Socket套接字(TCP/UDP)进行通信,要求包含文件的基本操作,例如打开和读写操作;
-
加密:要求发送方从文件读取内容,加密后并传输;接收方收到密文并解密,保存在文件中;
-
托管:程序代码托管到码云。
二、实验过程及结果
实验思路
- 先创建服务端和客户端,实现一个服务端对应一个客户端的功能,再尝试一个服务端响应多个客户端;
- 构建循环,使通信可以进行多次;
- 分别针对服务端和客户端,编写代码,实现文件传输与接受;
- 运用AES,实现对文件的加密和解密;
- 整合上述功能,形成完整程序。
监听多个客户端
创建服务端
服务端
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)
运行结果
通过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!")
对文件的加密和解密
我选择使用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,将多余位数的’ ’替换为空
def add_to_16(value):# str不是16的倍数那就补足为16的倍数
while len(value) % 16 != 0:
value += ' '
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('
Please 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(' ', '') # 执行解密
decrypted_text1 =decrypted_text.decode('utf-8')
print("
File has been decrypted successfully! the contents of the file are:")
print(decrypted_text1)
运行结果
服务端
客户端
三、实验过程中遇到的问题和解决过程
-
问题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
,即可 -
问题2:解密时报错
这段代码在运行时会报错:UnicodeDecodeError: ‘utf-8’ codec can’t decode byte 0xa1 in position 0: invalid start byte -
问题2 分析:最初怀疑是用于加密的txt并不是用‘utf-8’保存的,或含有非法字符,但经检验可以排查这种假设
后经查找资料,认为可能是解码对参数的控制过于严格,我们不能改变errors参数的默认值,但可以覆盖默认错误=“strict”的处理程序
但这样修改后,程序运行后会出现无法识别的字符
-
问题1解决方法:加一个eval()就可以了
四、其他(感悟、思考等)
算是做的最艰难的一个实验了,因为之前下载过很多个python的锅,实验开始没多久,pycharm就崩了,pycharm小白查方法查了很久才修好。虽然实验思路出来得很快,但面对网络上各种各样的代码,整合的过程对我来说难度很大。尤其是接连报错的时候,是换一份代码来copy还是给现有的代码缝缝补补是个很值得考虑的问题。爆肝几个晚上搞好,最后往码云push的时候,电脑十分适时地坏了,本地D盘中一切与"python"有关的东西统统丢掉了。。本实验被迫重做,写了四分之三的实验报告被迫重写,找好的资料被迫重找。。
感觉做大的项目,自信心和意志力甚至是比敲代码能力更重要的东西。在艰难的时候不放弃自己,遇到无法挽回的局面时有随时重新来过的勇气,这样才能走的更远吧。
五、参考资料
- PYTHON SOCKET编程详细介绍:https://www.cnblogs.com/wumingxiaoyao/p/7047658.html
- 下载Crypto:https://www.cnblogs.com/shanghongyun/p/11494052.html
- AES加密算法的详细介绍与实现:https://blog.csdn.net/qq_28205153/article/details/55798628
- 转码问题的解决的解决方法:“ignore”:https://blog.csdn.net/m0_37374307/article/details/80319146
- Python 加密文件与解密文件:https://blog.csdn.net/weixin_42930696/article/details/103722488?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.nonecase
- python3.6 实现AES加密、解密:https://blog.csdn.net/s740556472/article/details/79026128?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.nonecase