FTP 作业整理
一、FTP 客户端 与服务器端(没有解决黏包问题的代码)
服务器端设置
import socket import json ADDR = ('127.0.0.1',8081) sk =socket.socket() sk.bind(ADDR) sk.listen() conn,addr =sk.accept() content = conn.recv(1024).decode('utf-8') #接收文件的信息 content_dic =json.loads(content) #将文件信息loads出来形成字典格式. print(content_dic,type(content_dic)) #打印查看一下loads的内容(字典) if content_dic['operate'] =='upload': conn.send(b'received!') with open(content_dic['file_name'],'wb') as f :#打开新建一个文件,名称和要上传的文件一样,以bytes类型的模式进行bytes类型写操作. while content_dic['file_size']:#循环,直到文件的大小为0 file = conn.recv(1024) #接收文件每次以1024个byte的大小进行接收. f.write(file) #将接收到的文件写到创建的文件中 content_dic['file_size']-=len(file) #文件大小每次循环减 conn.close()#关闭链接 sk.close()#关闭 socket对象链接.
客户端设置代码
import socket import os import json sk=socket.socket() ADDR =('127.0.0.1',8081) sk.connect(ADDR) def get_filename(file_path): file_name =os.path.basename(file_path) return file_name operate =['upload','download'] for num,opt in enumerate(operate,1): print(num,opt) num = int( input("请输入您要进行操作的序号:")) file_path = input('请输入要传的文件:') file_size = os.path.getsize(file_path) file_name =get_filename(file_path) if num==1:#上传操作. dic ={ 'operate': 'upload','file_name':file_name, 'file_size':file_size} #创建一个字典包括了 操作,文件名,和文件大小. dic_str =json.dumps(dic) #将字典进行json操作转换成字符串. sk.send(dic_str.encode('utf-8'))
#发送upload的字典信息,包括了文件名,路径和文件大小.
第一次send(上面代码)
sk.recv(1024)
with open(file_path,'rb') as f :#打开想要上传的文件,以读的方式. while file_size:#(判断文件的大小是否非空,直到循环减为空) content = f.read(1024) #读出文件 1024个字节. sk.send(content) #将 1024个字节发送到服务器端. file_size-= len(content) #文件大小减到0 为止,停止循环.
第二次send(上面的代码)
运行结果
二、FTP 上传加入黏包知识点
借助struct模块,我们知道长度数字可以被转换成一个标准大小的4字节数字。因此可以利用这个特点来预先发送数据长度。
发送时 | 接收时 |
先发送struct转换好的数据长度4字节 | 先接受4个字节使用struct转换成数字来获取要接收的数据长度 |
再发送数据 | 再按照长度接收数据 |
服务器端设置
import socket import json import struct ADDR = ('127.0.0.1',8081) sk =socket.socket() sk.bind(ADDR) sk.listen() conn,addr =sk.accept() len_4 = conn.recv(4) len_dic =struct.unpack('i',len_4)[0]#获取到内容的字节大小. content =conn.recv(len_dic).decode('utf-8')# 在接收内容大小. content_dic =json.loads(content) #将文件信息loads出来形成字典格式. print(content_dic,type(content_dic)) #打印查看一下loads的内容(字典) if content_dic['operate'] =='upload': conn.send(b'received!') with open(content_dic['file_name'],'wb') as f :#打开新建一个文件,名称和要上传的文件一样,以bytes类型的模式进行bytes类型写操作. while content_dic['file_size']:#循环,直到文件的大小为0 file = conn.recv(1024) #接收文件每次以1024个byte的大小进行接收. f.write(file) #将接收到的文件写到创建的文件中 content_dic['file_size']-=len(file) #文件大小每次循环减 conn.close()#关闭链接 sk.close()#关闭 socket对象链
客户端
import socket import os import json import struct sk=socket.socket() ADDR =('127.0.0.1',8081) sk.connect(ADDR) def get_filename(file_path): file_name =os.path.basename(file_path) return file_name operate =['upload','download'] for num,opt in enumerate(operate,1): print(num,opt) num = int( input("请输入您要进行操作的序号:")) file_path = input('请输入要传的文件:') file_size = os.path.getsize(file_path) file_name =get_filename(file_path) if num==1:#上传操作. dic ={ 'operate': 'upload','file_name':file_name, 'file_size':file_size} #创建一个字典包括了 操作,文件名,和文件大小. dic_str =json.dumps(dic).encode('utf-8') #将字典进行json操作转换成字符串. ret =struct.pack('i', len(dic_str))#将字典的大小转换成一个定长的4个字节的大小. sk.send(ret+dic_str) #将字典的大小和内容发送出去. with open(file_path,'rb') as f :#打开想要上传的文件,以读的方式. while file_size:#(判断文件的大小是否非空,直到循环减为空) content = f.read(1024) #读出文件 1024个字节. sk.send(content) #将 1024个字节发送到服务器端. file_size-= len(content) #文件大小减到0 为止,停止循环. sk.close()
输出结果: