服务端代码:程序目录server/server.py 上传文件目录:server/upload
import os import time import socket import selectors #封装了一些相应的操作 BASE_DIR=os.path.dirname(os.path.abspath(__file__)) #第二步: class selectFtpServer: def __init__(self): self.dic = {} #创建空字典 self.hasReceived=0 self.sel = selectors.DefaultSelector() #通过selectors模块下的DefaultSelector这个类拿I/O多路方法拿到实例对象变成实例变量self.sel self.create_socket() #create_socket()是创建socket对象函数完成绑定功能 self.handle() #handle()函数完成循环监听 #create_socket函数是用来创建socket对象完成绑定功能 def create_socket(self): server = socket.socket() #创建socket对象赋值server server.bind(('192.168.1.160', 8080)) #绑定 server.listen(5) #监听 server.setblocking(False) #设置非阻塞 self.sel.register(server, selectors.EVENT_READ,self.accept) #有了server对象通过self.sel实例变量register注册完成绑定功能(server跟self.accept做绑定) print("服务端已启动,等待客户端连接.....") #handle()函数完成循环监听 def handle(self): while True: #第一步:程序启动后走while循环 #第七步:运行完accept函数后到这里 #所有操作围绕一个对象self.sel实例变量核心对象展开的 events = self.sel.select() #第二步:调用self.sel实例变量,监听的内容server封装到events对象里 #第八步:如果客户端发过来此刻监听的内容就有变化有俩个对象server和conn封装到events对象里 for key, mask in events: #第三步:for循环events(可迭代对象)拿到key和mask #第九步:for循环events(可迭代对象)拿到key和mask callback = key.data #第四步:当前key.data是self.accept函数赋值给callback #第十步:当前key.data是read函数赋值给callback #key.fileobj是拿到的监听的对象。 callback(key.fileobj, mask) #第五步:运行callback执行accpt()函数 #第十一步:运行callback执行read()函数里面放到的之前链接相应的文件描述符conn #print('当前监听的对象:', key.fileobj) #第六步:accept函数接收连接服务端的客户端信息 def accept(self,sock,mask): #接收sock和mask conn, addr = sock.accept() #sock.accept接收此时链接服务端的socket对象拿到conn和addr #print('accepted', conn, 'from', addr) print("from %s %s connected" %addr) conn.setblocking(False) #设置成非阻塞 self.sel.register(conn, selectors.EVENT_READ, self.read) #把conn跟read做绑定后程序返回到handle()函数里的events继续监听 #print(conn) self.dic[conn] = {} #在空字典里进行了conn赋值,self.dic={conn:{},} #第十二步:read函数返回给客户端ok def read(self,conn, mask): #接收了conn和mask try: #加异常防止客户端突然断开 if not self.dic[conn]: #判断self.dic[conn]里面是否是空字典,如果是空字典,代表第一次进来 data = conn.recv(1024) #conn接收了客户端发来的数据 cmd,filename,filesize = str(data,encoding='utf-8').split('|') #把接收到客户端发来的包解开拿到cmd,filename,filesize个信息 self.dic={conn:{"cmd" : cmd, "filename" : filename, "filesize" : int(filesize)}} #把拿到的cmd,filename,filesize信息放到self.dic字典里去后程序返回到handle()函数里的events继续监听 if cmd == 'put': #如果接收的信息是put conn.send(bytes("OK",encoding='utf8')) #给客户端返回一条数据 if self.dic[conn]['cmd'] == 'get': file = os.path.join(BASE_DIR,"upload",filename) if os.path.exists(file): fileSize = os.path.getsize(file) send_info = '%s|%s' %('YES',fileSize) conn.send(bytes(send_info, encoding='utf8')) else: send_info = '%s|%s' %('NO',0) conn.send(bytes(send_info, encoding='utf8')) else: #如果不是空字典代表不是第一次进来 if self.dic[conn].get('cmd',None): #对接收的命令进行分发判断是put还是get cmd=self.dic[conn].get('cmd') if hasattr(self, cmd): #如果cmd=put调用put函数,如果是cmd=get函数调用get函数 func = getattr(self,cmd) func(conn) else: print("error cmd!") conn.close() else: print("error cmd!") conn.close() except Exception as e: print('断开的客户端信息是:', conn) self.sel.unregister(conn) #如果没有接收到数据做一个关闭解除 conn.close() #put上传函数 def put(self, conn): fileName = self.dic[conn]['filename'] fileSize = self.dic[conn]['filesize'] path = os.path.join(BASE_DIR,"upload",fileName) #拿到要接收的信息 #print(fileName,fileSize,path) recv_data = conn.recv(1024) #接收客户端上传的数据1024字节 self.hasReceived += len(recv_data) #把接收的数据累加到变量self.hasReceived with open(path, 'ab') as f: #打开文件 f.write(recv_data) #把接收的数据写到文件里去 if fileSize == self.hasReceived: #判断文件大小跟接收大小是否一样 if conn in self.dic.keys(): #如果文件大小跟接收大小一样清空字典 self.dic[conn] = {} print("%s 上传完毕!" %fileName) if __name__=='__main__': selectFtpServer() #第一步:实例化触发selectFtpServer这个类
客户端代码:目录结果:/client/clenb.py
import socket import os,sys BASE_DIR=os.path.dirname(os.path.abspath(__file__)) class selectFtpClient: def __init__(self): self.args=sys.argv #sys.argv在命令行输入的参数,第一个参数默认文件名,第二个参数跟IP地址和端口 if len(self.args)>1: #如果大于1把第二个参数俩个值赋值给port self.port=(self.args[1],int(self.args[2])) else: self.port=("192.168.1.160",8080) #如果没有第二个参数默认取这个 self.create_socket() # self.command_fanout() #进行命令分发 #create_socket函数创建socket对象连接服务端 def create_socket(self): try: self.sk = socket.socket() self.sk.connect(self.port) print('连接FTP服务器成功!') except Exception as e: print("eroor:",e) #command_fanout()函数进行命令分发 def command_fanout(self): while True: cmd = input('>>>').strip() #引导用户输入上传还是下载 if cmd == 'exit()': break cmd,file = cmd.split() #把输入的命令分开进行反射 if hasattr(self,cmd): func = getattr(self,cmd) func(cmd,file) else: print('调用错误!') #put()上传函数 def put(self,cmd,file): if os.path.isfile(file): #判断本地文件是否存在 fileName = os.path.basename(file) #取出文件的名字 fileSize = os.path.getsize(file) #取出文件的大小 fileInfo = '%s|%s|%s'%(cmd,fileName,fileSize) #给文件名字大小打包成fileInf self.sk.send(bytes(fileInfo, encoding='utf8')) #调用send方法把fileInf发给服务端 recvStatus = self.sk.recv(1024) #接收服务端返回的OK内容 print('recvStatus' , recvStatus) hasSend = 0 if str(recvStatus, encoding='utf8') == "OK": #如果接收到服务端返回的OK with open(file, 'rb') as f: #打开文件 while fileSize > hasSend : #循环的去上传文件 contant = f.read(1024) recv_size = len(contant) self.sk.send(contant) hasSend += recv_size s=str(int(hasSend/fileSize*100))+"%" print("正在上传文件: "+fileName+" 已经上传:" +s) print('%s文件上传完毕' % (fileName,)) else: print('文件不存在') #get()下载函数 def get(self,cmd,file): pass if __name__=='__main__': selectFtpClient()