Home > Article > Backend Development > Tutorial on implementing FTP using socket
1、用户加密认证 2、允许同时多用户登录 3、每个用户有自己的家目录 ,且只能访问自己的家目录 4、对用户进行磁盘配额,每个用户的可用空间不同 5、允许用户在ftp server上随意切换目录 6、允许用户查看当前目录下文件 7、允许上传和下载文件,保证文件一致性 8、文件传输过程中显示进度条 附加功能:支持文件的断点续传 python 3.5 用socketserver实现FTP ConfigParser 是Python自带的模块, 用来读写配置文件,将用户信息以下边的格式存入account.py文件,读出后进行判断
[DEFAULT] [alex] Password = 123456Quotation = 100[jack] Password = 123456Quotation = 100
2、允许同时多用户登录 从scokerserver 继承 socketserver.ThreadingTCPServer即可实现
3、每个用户有自己的家目录 ,且只能访问自己的家目录 将所有路径封装在home目录下
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) USER_HOME = '%s\home' % BASE_DIR LOG_DIR = '%s\log' % BASE_DIR LOG_LEVEL = 'DEBUG'
4、允许用户在ftp server上随意切换目录 用os模块改变工作目录
os.chdir(func_val)
5、允许用户查看当前目录下文件 用os模块的os.listdir,下为server端代码
def _ls(self,*args,**kwargs):'''显示当前目录下的所有文件'''if os.getcwd() == '%s\\bin'% settings.BASE_DIR: user_home_dir = "%s\%s" % (settings.USER_HOME, self.user["Username"]) self.request.send(json.dumps(os.listdir(user_home_dir)).encode())else:self.request.send(json.dumps(os.listdir()).encode())
6、允许上传和下载文件,保证文件一致性 判断上传和下载的文件在传输前后的大小,用以判断文件是否保持一致
1 #server端 2 # 3 def _put(self,*args,**kwargs): 4 '''上传文件命令''' 5 data = args[0] 6 7 response = self.get_response() 8 if response["status_code"] == 257: # ready to receive 9 self.request.send(b'1') # send confirmation to server 10 user_home_dir = "%s\%s" % (settings.USER_HOME, self.user["Username"]) 11 base_filename = "%s\%s" % (user_home_dir, data.get('filename')) 12 received_size = 0 13 file_obj = open(base_filename, 'wb') 14 if data.get('md5'): 15 md5_obj = hashlib.md5() 16 while received_size < response['file_size']: 17 line = self.request.recv(4096) 18 received_size += len(line) 19 file_obj.write(line) 20 md5_obj.update(line) 21 else: 22 file_obj.close() 23 md5_val = md5_obj.hexdigest() 24 self.send_response(258, {'md5': md5_val}) 25 print("-----file rece done-----") 26 27 else: 28 while received_size < response['file_size']: 29 data = self.request.recv(4096) 30 received_size += len(data) 31 file_obj.write(data) 32 else: 33 print("-----file rece done-----") 34 file_obj.close() 35 else: 36 print(STATUS_CODE[response["status_code"]]) 37 38 39 def _get(self,*args,**kwargs): 40 '''下载文件命令''' 41 data = args[0] 42 if data.get('filename') is None: 43 self.send_response(255) 44 user_home_dir = "%s\%s" %(settings.USER_HOME,self.user["Username"]) 45 file_abs_path = "%s\%s" %(user_home_dir,data.get('filename')) 46 print("file abs path",file_abs_path) 47 48 if os.path.isfile(file_abs_path): 49 file_obj = open(file_abs_path,'rb') 50 file_size = os.path.getsize(file_abs_path) 51 self.send_response(257,data = {'file_size':file_size}) 52 self.request.recv(1) #等待客户端确认,防粘包 53 54 if data.get('md5'): 55 md5_obj = hashlib.md5() 56 for line in file_obj: 57 self.request.send(line) 58 md5_obj.update(line) 59 else: 60 file_obj.close() 61 md5_val = md5_obj.hexdigest() 62 self.send_response(258,{'md5':md5_val}) 63 print("send file done...") 64 else: 65 for line in file_obj: 66 self.request.send(line) 67 else: 68 file_obj.close() 69 print("send file done...") 70 else: 71 self.send_response(256) 72 73 74 75 76 # client端 77 # 78 def _get(self,cmd_list): 79 '''下载文件''' 80 print("get--",cmd_list) 81 if len(cmd_list) == 1: 82 print("no filename follows...") 83 return 84 data_header = { 85 'action':'get', 86 'filename':cmd_list[1] 87 } 88 if self.__md5_required(cmd_list): 89 data_header['md5'] = True 90 91 self.sock.send(json.dumps(data_header).encode()) 92 response = self.get_response() 93 print(response) 94 try: 95 if response["status_code"] == 257:#ready to receive 96 self.sock.send(b'1') #send confirmation to server 97 base_filename = cmd_list[1].split('/')[-1] 98 received_size = 0 99 file_obj = open(base_filename,'wb')100 if self.__md5_required(cmd_list):101 md5_obj = hashlib.md5()102 progress = self.show_progress(response['file_size']) #generator103 progress.__next__()104 while received_size < response['file_size']:105 data = self.sock.recv(4096)106 received_size += len(data)107 try:108 progress.send(len(data))109 except StopIteration as e:110 print("100%")111 file_obj.write(data)112 md5_obj.update(data)113 else:114 print("-----file rece done-----")115 file_obj.close()116 md5_val = md5_obj.hexdigest()117 md5_from_server = self.get_response()118 if md5_from_server['status_code'] == 258:119 if md5_from_server['md5'] == md5_val:120 print("%s 文件一致性校验成功!"% base_filename)121 print(md5_val,md5_from_server)122 123 else:124 progress = self.show_progress(response['file_size']) # generator125 progress.__next__()126 while received_size < response['file_size']:127 data = self.sock.recv(4096)128 received_size += len(data)129 file_obj.write(data)130 try:131 progress.send(len(data))132 except StopIteration as e:133 print("100%")134 else:135 print("-----file rece done-----")136 file_obj.close()137 else:138 print(STATUS_CODE[response["status_code"]])139 except Exception as e:140 base_file_size = os.path.getsize(base_filename)141 with open('data\\breakpoint', 'wb') as br_po:142 data_header['action'] = 'breakpoint'143 data_header['breakpoint'] = base_file_size144 br_po.write(json.dumps(data_header).encode())145 146 147 def _put(self,cmd_list):148 '''上传文件'''149 print("put--", cmd_list)150 if len(cmd_list) == 1:151 print("no filename follows...")152 return153 data_header = {154 'action': 'put',155 'filename': cmd_list[1]156 }157 if self.__md5_required(cmd_list):158 data_header['md5'] = True159 160 self.sock.send(json.dumps(data_header).encode())161 if os.path.isfile(cmd_list[1]):162 file_obj = open(cmd_list[1], 'rb')163 file_size = os.path.getsize(cmd_list[1])164 self.send_response(257, data={'file_size': file_size})165 self.sock.recv(1) # 等待服务器端确认166 if self.__md5_required(cmd_list):167 md5_obj = hashlib.md5()168 progress = self.show_progress(file_size) # generator169 progress.__next__()170 for line in file_obj:171 self.sock.send(line)172 try:173 progress.send(len(line))174 except StopIteration as e:175 print("100%")176 md5_obj.update(line)177 else:178 file_obj.close()179 md5_val = md5_obj.hexdigest()180 md5_from_server = self.get_response()181 if md5_from_server['md5'] == md5_val:182 print("%s 文件一致性校验成功!"% cmd_list[1])183 self.send_response(258, {'md5': md5_val})184 print("send file done...")185 else:186 progress = self.show_progress(file_size) # generator187 progress.__next__()188 for line in file_obj:189 self.sock.send(line)190 try:191 progress.send(len(line))192 except StopIteration as e:193 print("100%")194 else:195 file_obj.close()196 print("send file done...")197 else:198 print(256)
7、文件传输过程中显示进度条 根据已传文件大小和总文件大小比对判断,输出符号
1 def show_progress(self,total): 2 '''显示进度条 3 total: 文件大小''' 4 received_size = 0 5 current_percent = 0 6 while received_size < total: 7 if int((received_size / total) * 100) > current_percent: 8 print(">",end='',flush=True) 9 current_percent = int((received_size / total) * 100)10 new_size = yield11 received_size += new_size
附加功能:支持文件的断点续传 将上次断点信息记录下来,也就是记录已传文件大小,再次启用时,将目标文件光标移到上次断点处,然后进行续传
1 def _breakpoint(self,*args,**kwargs): 2 '''断点续传''' 3 data = args[0] 4 if data.get('filename') is None: 5 self.send_response(255) 6 user_home_dir = "%s\%s" % (settings.USER_HOME, self.user["Username"]) 7 file_abs_path = "%s\%s" % (user_home_dir, data.get('filename')) 8 print("file abs path", file_abs_path) 9 print(data.get('breakpoint'))10 11 if os.path.isfile(file_abs_path):12 file_obj = open(file_abs_path, 'rb')13 file_obj.seek(data.get('breakpoint'))14 file_size = os.path.getsize(file_abs_path)15 self.send_response(257, data={'file_size': file_size})16 self.request.recv(1) # 等待客户端确认17 18 if data.get('md5'):19 md5_obj = hashlib.md5()20 for line in file_obj:21 self.request.send(line)22 md5_obj.update(line)23 else:24 file_obj.close()25 md5_val = md5_obj.hexdigest()26 self.send_response(258, {'md5': md5_val})27 print("send file done...")28 else:29 for line in file_obj:30 self.request.send(line)31 else:32 file_obj.close()33 print("send file done...")34 else:35 self.send_response(256)36 pass
主要知识点:
类的继承 os模块的应用 json模块的应用 类方法的运用 md5加密方法 scoket链接 scoketserver链接 反射 异常处理 小结: 这个程序主要用socketserver实现了一台服务器链接多个客户端,并且进行信息交互,并且实现了几个简单的功能,如:get 文件下载、put 上传文件、cd 切换目录、ls 查看文件、breakpoint断点续传。 主要代码:
1 #server端 2 # 3 4 5 import os,sys 6 import hashlib 7 import socket 8 import socketserver 9 import json 10 import configparser 11 from conf import settings 12 13 14 STATUS_CODE = { 15 250 : "Invalid cmd format,e.g:{'action':'get','filename':'test.py','size':344", 16 251 : "Invalid cmd", 17 252 : "Invalid auth data", 18 253 : "Wrong username or password..", 19 254 : "Passed authentication", 20 255 : "Filename doesn't provided", 21 256 : "File doesn't exist on server", 22 257 : "ready to send file", 23 258 : "md5 verification", 24 259 : "Directory has been switched" 25 } 26 27 28 29 30 31 class FTPHandler(socketserver.BaseRequestHandler): 32 '''定义request handler类,从BaseRequestHandler类继承''' 33 def handle(self): 34 ''' 35 获取服务器端的信息 36 如果传来cd命令,改变工作目录 37 ''' 38 39 while True: 40 self.data = self.request.recv(1024).strip() 41 if not self.data: 42 print('Client closed..') 43 break 44 data = json.loads(self.data.decode()) 45 if data.get('action') is not None: 46 if hasattr(self,'_%s'%data.get('action')): 47 func =getattr(self,'_%s'%data.get('action')) 48 func_val = func(data) 49 if data.get('action') == 'cd': #cd 命令,改变工作目录 50 os.chdir(func_val) 51 else:pass 52 else: 53 print('Invalid cmd') 54 self.send_response(251) 55 else: 56 print('Invalid cmd format') 57 self.send_response(250) 58 59 60 def send_response(self,status_code,data=None): 61 '''向客户端返回数据''' 62 response = {'status_code': status_code, 'status_msg':STATUS_CODE[status_code]} 63 if data: 64 response.update(data) 65 self.request.send(json.dumps(response).encode()) 66 67 68 def _auth(self,*args,**kwargs): 69 '''判断用户是否输入完整的用户名和密码 70 验证用户名和密码是否合法''' 71 data = args[0] 72 if data.get('username') is None or data.get('password') is None: 73 self.send_response(252) 74 75 user = self.authenticate(data.get('username'),data.get('password')) 76 if user is None: 77 self.send_response(253) 78 else: 79 print('passed authentication',user) 80 self.user = user 81 self.send_response(254) 82 83 84 def authenticate(self,username,password): # 85 '''验证用户合法性,合法返回用户数据''' 86 config = configparser.ConfigParser() 87 config.read(settings.ACCOUNT_FILE) 88 if username in config.sections(): 89 _password = config[username]['Password'] 90 if _password == password: 91 print('pass auth..',username) 92 config[username]["Username"] = username 93 return config[username] 94 95 96 def get_response(self): 97 '''接收客户端回复结果''' 98 data = self.request.recv(1024) 99 data = json.loads(data.decode())100 return data101 102 103 def show_progress(self,total):104 '''显示进度条105 total: 文件大小'''106 received_size = 0107 current_percent = 0108 while received_size < total:109 if int((received_size / total) * 100) > current_percent:110 print(">",end='',flush=True)111 current_percent = int((received_size / total) * 100)112 new_size = yield113 received_size += new_size114 115 116 def _put(self,*args,**kwargs):117 '''上传文件命令'''118 data = args[0]119 120 response = self.get_response()121 if response["status_code"] == 257: # ready to receive122 self.request.send(b'1') # send confirmation to server123 user_home_dir = "%s\%s" % (settings.USER_HOME, self.user["Username"])124 base_filename = "%s\%s" % (user_home_dir, data.get('filename'))125 received_size = 0126 file_obj = open(base_filename, 'wb')127 if data.get('md5'):128 md5_obj = hashlib.md5()129 while received_size < response['file_size']:130 line = self.request.recv(4096)131 received_size += len(line)132 file_obj.write(line)133 md5_obj.update(line)134 else:135 file_obj.close()136 md5_val = md5_obj.hexdigest()137 self.send_response(258, {'md5': md5_val})138 print("-----file rece done-----")139 140 else:141 while received_size < response['file_size']:142 data = self.request.recv(4096)143 received_size += len(data)144 file_obj.write(data)145 else:146 print("-----file rece done-----")147 file_obj.close()148 else:149 print(STATUS_CODE[response["status_code"]])150 151 152 def _get(self,*args,**kwargs):153 '''下载文件命令'''154 data = args[0]155 if data.get('filename') is None:156 self.send_response(255)157 user_home_dir = "%s\%s" %(settings.USER_HOME,self.user["Username"])158 file_abs_path = "%s\%s" %(user_home_dir,data.get('filename'))159 print("file abs path",file_abs_path)160 161 if os.path.isfile(file_abs_path):162 file_obj = open(file_abs_path,'rb')163 file_size = os.path.getsize(file_abs_path)164 self.send_response(257,data = {'file_size':file_size})165 self.request.recv(1) #等待客户端确认,防粘包166 167 if data.get('md5'):168 md5_obj = hashlib.md5()169 for line in file_obj:170 self.request.send(line)171 md5_obj.update(line)172 else:173 file_obj.close()174 md5_val = md5_obj.hexdigest()175 self.send_response(258,{'md5':md5_val})176 print("send file done...")177 else:178 for line in file_obj:179 self.request.send(line)180 else:181 file_obj.close()182 print("send file done...")183 else:184 self.send_response(256)185 186 187 188 def _ls(self,*args,**kwargs):189 '''显示当前目录下的所有文件'''190 if os.getcwd() == '%s\\bin'% settings.BASE_DIR:191 user_home_dir = "%s\%s" % (settings.USER_HOME, self.user["Username"])192 self.request.send(json.dumps(os.listdir(user_home_dir)).encode())193 else:self.request.send(json.dumps(os.listdir()).encode())194 195 196 197 def _cd(self,*args,**kwargs):198 '''改变工作目录'''199 data = args[0]200 user_home_dir = "%s\%s" % (settings.USER_HOME, self.user["Username"])201 file_abs_path = "%s\%s" %(user_home_dir,data.get('path'))202 try:203 os.listdir(file_abs_path)204 except FileNotFoundError as e:205 self.request.send(json.dumps(e).encode())206 return207 self.request.send(json.dumps(259).encode())208 os.chdir(file_abs_path)209 return file_abs_path210 211 212 def _breakpoint(self,*args,**kwargs):213 '''断点续传'''214 data = args[0]215 if data.get('filename') is None:216 self.send_response(255)217 user_home_dir = "%s\%s" % (settings.USER_HOME, self.user["Username"])218 file_abs_path = "%s\%s" % (user_home_dir, data.get('filename'))219 print("file abs path", file_abs_path)220 print(data.get('breakpoint'))221 222 if os.path.isfile(file_abs_path):223 file_obj = open(file_abs_path, 'rb')224 file_obj.seek(data.get('breakpoint'))225 file_size = os.path.getsize(file_abs_path)226 self.send_response(257, data={'file_size': file_size})227 self.request.recv(1) # 等待客户端确认228 229 if data.get('md5'):230 md5_obj = hashlib.md5()231 for line in file_obj:232 self.request.send(line)233 md5_obj.update(line)234 else:235 file_obj.close()236 md5_val = md5_obj.hexdigest()237 self.send_response(258, {'md5': md5_val})238 print("send file done...")239 else:240 for line in file_obj:241 self.request.send(line)242 else:243 file_obj.close()244 print("send file done...")245 else:246 self.send_response(256)247 pass
1 #client端 2 # 3 4 import socket 5 import hashlib 6 import os,json 7 import optparse 8 import socketserver 9 10 11 STATUS_CODE = { 12 250 : "Invalid cmd format,e.g:{'action':'get','filename':'test.py','size':344", 13 251 : "Invalid cmd", 14 252 : "Invalid auth data", 15 253 : "Wrong username or password..", 16 254 : "Passed authentication", 17 255 : "Filename doesn't provided", 18 256 : "File doesn't exist on server", 19 257 : "ready to send file", 20 258 : "md5 verification", 21 259 : "Directory has been switched" 22 } 23 24 25 26 27 class FTPClient(object): 28 '''客户端''' 29 def __init__(self): 30 '''用户信息输入格式化 31 变量定义''' 32 parser = optparse.OptionParser() 33 parser.add_option('-s', '--server', dest='server', help ='ftp server ip_addr') 34 parser.add_option('-p','--port', type='int', dest='port', help='ftp server port') 35 parser.add_option('-U', '--username', dest='username', help='username') 36 parser.add_option('-P', '--Password', dest='password', help='password') 37 self.options, self.args = parser.parse_args() 38 self.verify_args(self.options,self.args) 39 self.make_connection() 40 41 42 def make_connection(self): 43 '''连接服务器''' 44 self.sock = socket.socket() 45 self.sock.connect((self.options.server,self.options.port)) 46 47 48 49 def verify_args(self,options,args): 50 '''校验参数合法性''' 51 if options.username and options.password: 52 pass 53 elif options.username is None and options.password is None: 54 pass 55 else: 56 if options.username is None or options.password is None: 57 print('Error:username and password must be provided together..') 58 if options.server and options.port: 59 if options.port > 0 and options.port < 65535: 60 return True 61 else: 62 exit('Err:host port must in 0-65535') 63 64 65 def authenticate(self): 66 '''用户验证''' 67 if self.options.username: 68 return self.get_auth_result(self.options.username, self.options.password) 69 else: 70 retry_count = 0 71 while retry_count < 3: 72 username = input("username:").strip() 73 password = input("password:").strip() 74 return self.get_auth_result(username,password) 75 76 77 78 79 def get_auth_result(self,user,password): 80 '''用户认证''' 81 data = {'action':'auth', 82 'username':user, 83 'password':password} 84 85 self.sock.send(json.dumps(data).encode()) 86 response = self.get_response() 87 if response.get('status_code') == 254: 88 print("Passed authentication!") 89 self.user = user 90 return True 91 else: 92 print(data.get('status_msg')) 93 print('response:',data) 94 95 96 def __md5_required(self,cmd_list): 97 '''检测命令是否需要进行MD5验证''' 98 if '--md5' in cmd_list: 99 return True100 101 102 def show_progress(self,total):103 '''进度条'''104 received_size = 0105 current_percent = 0106 while received_size < total:107 if int((received_size / total) * 100) > current_percent:108 print(">",end='',flush=True)109 current_percent = int((received_size / total) * 100)110 new_size = yield111 received_size += new_size112 113 114 def send_response(self,status_code,data=None):115 '''向服务器端返回数据'''116 response = {'status_code': status_code, 'status_msg':STATUS_CODE[status_code]}117 if data:118 response.update(data)119 self.sock.send(json.dumps(response).encode())120 121 122 def get_response(self):123 '''得到服务器端回复结果'''124 data = self.sock.recv(1024)125 data = json.loads(data.decode())126 return data127 128 129 def _get(self,cmd_list):130 '''下载文件'''131 print("get--",cmd_list)132 if len(cmd_list) == 1:133 print("no filename follows...")134 return135 data_header = {136 'action':'get',137 'filename':cmd_list[1]138 }139 if self.__md5_required(cmd_list):140 data_header['md5'] = True141 142 self.sock.send(json.dumps(data_header).encode())143 response = self.get_response()144 print(response)145 try:146 if response["status_code"] == 257:#ready to receive147 self.sock.send(b'1') #send confirmation to server148 base_filename = cmd_list[1].split('/')[-1]149 received_size = 0150 file_obj = open(base_filename,'wb')151 if self.__md5_required(cmd_list):152 md5_obj = hashlib.md5()153 progress = self.show_progress(response['file_size']) #generator154 progress.__next__()155 while received_size < response['file_size']:156 data = self.sock.recv(4096)157 received_size += len(data)158 try:159 progress.send(len(data))160 except StopIteration as e:161 print("100%")162 file_obj.write(data)163 md5_obj.update(data)164 else:165 print("-----file rece done-----")166 file_obj.close()167 md5_val = md5_obj.hexdigest()168 md5_from_server = self.get_response()169 if md5_from_server['status_code'] == 258:170 if md5_from_server['md5'] == md5_val:171 print("%s 文件一致性校验成功!"% base_filename)172 print(md5_val,md5_from_server)173 174 else:175 progress = self.show_progress(response['file_size']) # generator176 progress.__next__()177 while received_size < response['file_size']:178 data = self.sock.recv(4096)179 received_size += len(data)180 file_obj.write(data)181 try:182 progress.send(len(data))183 except StopIteration as e:184 print("100%")185 else:186 print("-----file rece done-----")187 file_obj.close()188 else:189 print(STATUS_CODE[response["status_code"]])190 except Exception as e:191 base_file_size = os.path.getsize(base_filename)192 with open('data\\breakpoint', 'wb') as br_po:193 data_header['action'] = 'breakpoint'194 data_header['breakpoint'] = base_file_size195 br_po.write(json.dumps(data_header).encode())196 197 198 def _put(self,cmd_list):199 '''上传文件'''200 print("put--", cmd_list)201 if len(cmd_list) == 1:202 print("no filename follows...")203 return204 data_header = {205 'action': 'put',206 'filename': cmd_list[1]207 }208 if self.__md5_required(cmd_list):209 data_header['md5'] = True210 211 self.sock.send(json.dumps(data_header).encode())212 if os.path.isfile(cmd_list[1]):213 file_obj = open(cmd_list[1], 'rb')214 file_size = os.path.getsize(cmd_list[1])215 self.send_response(257, data={'file_size': file_size})216 self.sock.recv(1) # 等待服务器端确认217 if self.__md5_required(cmd_list):218 md5_obj = hashlib.md5()219 progress = self.show_progress(file_size) # generator220 progress.__next__()221 for line in file_obj:222 self.sock.send(line)223 try:224 progress.send(len(line))225 except StopIteration as e:226 print("100%")227 md5_obj.update(line)228 else:229 file_obj.close()230 md5_val = md5_obj.hexdigest()231 md5_from_server = self.get_response()232 if md5_from_server['md5'] == md5_val:233 print("%s 文件一致性校验成功!"% cmd_list[1])234 self.send_response(258, {'md5': md5_val})235 print("send file done...")236 else:237 progress = self.show_progress(file_size) # generator238 progress.__next__()239 for line in file_obj:240 self.sock.send(line)241 try:242 progress.send(len(line))243 except StopIteration as e:244 print("100%")245 else:246 file_obj.close()247 print("send file done...")248 else:249 print(256)250 251 252 def _ls(self,*args,**kwargs):253 '''获取当前目录下的所有文件'''254 print('ls'.center(30,'-'))255 data_header = {256 'action': 'ls',257 }258 self.sock.send(json.dumps(data_header).encode())259 ls_val = self.sock.recv(1024)260 ls_val = json.loads(ls_val.decode())261 for obj in ls_val:262 print(obj)263 264 265 def _cd(self,cmd_list):266 '''改变工作目录'''267 print("cd--", cmd_list)268 if len(cmd_list) == 1:269 cmd_list.append('')270 data_header = {271 'action': 'cd',272 'path': cmd_list[1]273 }274 self.sock.send(json.dumps(data_header).encode())275 server_path = self.sock.recv(1024)276 server_path = json.loads(server_path.decode())277 if server_path == 259 :278 print("%s >" % cmd_list[1],end='')279 280 281 def _breakpoint(self,*args,**kwargs):282 '''断点续传'''283 with open('data\\breakpoint', 'rb') as br_po:284 data_header = json.loads(br_po.read().decode())285 br_po.close()286 if data_header:287 print(data_header)288 self.sock.send(json.dumps(data_header).encode())289 response = self.get_response()290 try:291 if response["status_code"] == 257: # ready to receive292 self.sock.send(b'1') # send confirmation to server293 base_filename = data_header['filename'].split('/')[-1]294 received_size = data_header['breakpoint']295 file_obj = open(base_filename, 'ab')296 file_obj.seek(data_header['breakpoint'])297 if self.__md5_required(data_header):298 md5_obj = hashlib.md5()299 progress = self.show_progress(response['file_size']) # generator300 progress.__next__()301 while received_size < response['file_size']:302 data = self.sock.recv(4096)303 received_size += len(data)304 try:305 progress.send(len(data))306 except StopIteration as e:307 print("100%")308 file_obj.write(data)309 md5_obj.update(data)310 else:311 print("-----file rece done-----")312 file_obj.close()313 md5_val = md5_obj.hexdigest()314 md5_from_server = self.get_response()315 if md5_from_server['status_code'] == 258:316 if md5_from_server['md5'] == md5_val:317 print("%s 文件一致性校验成功!" % base_filename)318 with open('data\\breakpoint', 'wb') as br_po:319 br_po.write()320 print(md5_val, md5_from_server)321 322 323 324 else:325 progress = self.show_progress(response['file_size']) # generator326 progress.__next__()327 while received_size < response['file_size']:328 data = self.sock.recv(4096)329 received_size += len(data)330 file_obj.write(data)331 try:332 progress.send(len(data))333 except StopIteration as e:334 print("100%")335 else:336 print("-----file rece done-----")337 file_obj.close()338 with open('data\\breakpoint', 'wb') as br_po:339 br_po.write()340 except Exception as e:341 base_file_size = os.path.getsize(base_filename)342 with open('data\\breakpoint', 'wb') as br_po:343 data_header['breakpoint'] = base_file_size344 br_po.write(json.dumps(data_header).encode())345 else:print('There is no need to transfer files..')346 347 348 def _helps(self,*args,**kwargs):349 '''帮助信息'''350 helps = '''351 get 文件名 #下载文件352 put 文件名 #上传文件353 ls #获取当前目录下的所有文件354 cd #改变工作目录355 breakpoint #断点续传'''356 print(helps)357 358 359 def interactive(self):360 '''根据命令分配函数'''361 if self.authenticate():362 print('---start interactive iwth u...(helps 帮助信息)')363 while True:364 choice = input("[%s]:" % self.user).strip()365 if len(choice) == 0: continue366 cmd_list = choice.split()367 if hasattr(self, "_%s" % cmd_list[0]):368 func = getattr(self, "_%s" % cmd_list[0])369 func(cmd_list)370 else:371 print("Invalid cmd")372 373 374 375 if __name__ == '__main__':376 ftp = FTPClient()377 ftp.interactive()
Create a TCP server
Idea: create a socket---bind the socket to a local address---monitor the connection---answer TCP client requests in an infinite loop (will return to the client after receiving end socket)---Accept TCP data---Send TCP data---Close sub-socket---Close server socket
from socket import *
from time import ctime
HOST = '' # Indicates that any valid address can be bound
PORT = 2157
BUFSIZ = 1024 # Keyword, size is 1k
ADR= (HOST, PORT)
tcpSerSock = socket(AF_INET, SOCK_STREAM) # Create a network-based, TCP server socket
tcpSerSock.bind(ADR)
tcpSerSock.listen(5) # Only allow 5 connections at the same time
while True: # Infinite loop answer
print 'waiting for connection....'
tcpCliSock, addr = tcpSerSock.accept() # Receive and save the client socket and client address
print '...connected from:', addr
while True:
data = tcpCliSock.recv(BUFSIZ) # Read buffer data
if not data:
break
tcpCliSock.send('{0} {1}' .format(ctime(), data)) #Send information to the client
tcpCliSock.close()
tcpSerSock.close()
Create TCP client
Idea: Create a client socket---connect---send data (exit if the send is empty)---receive data from the buffer (exit if the reception fails)-- -Close client socket
from socket import *
HOST = 'localhost'
PORT = 21567
BUFSIZ = 1024
ADDR = (HOST, PORT)
tcpCliSock = socket(AF_INET, SOCK_STREAM)
tcpCliSock.connect(ADDR)
while True:
data = raw_input('>')
if not data:
break
tcpCliSock.send(data)
data = tcpCliSock.recv(BUFSIZ)
if not data:
break
print data
tcpCliSock.close()
Create a UDP server
Idea: create a socket---bind the socket to a local address--receive data---send data
from socket import *
from time import ctime
HOST = '' # Indicates that any valid address can be bound
PORT = 2157
BUFSIZ = 1024 # Keyword, size is 1k
ADR= (HOST, PORT)
udpSerSock = socket(AF_INET, SOCK_DGRAM)
udpSerSock.bind(ADR)
while True:
print 'waiting for message...'
data, addr = udpSerSock. recvfrom(BUFSIZ)
udpSerSock.sendto('{0} {1}' .format(ctime(), data), addr)
print '...received from and returned to:', addr
udpSerSock.close()
Create a UDp client
Idea: Create a client socket---send data---receive data
from socket import *
from time import ctime
HOST = 'localhost' # Indicates that any valid address can be bound
PORT = 2157
BUFSIZ = 1024 # Keyword, size is 1k
ADDR= (HOST, PORT)
udpCliSock = socket(AF_INET, SOCK_DGRAM)
while True:
data = raw_input('>')
if not data:
break
udpCliSock .sendto(data, ADDR)
data, ADDR = udpCliSock.recvfrom(BUFSIZ)
if not data:
break
print data
udpCliSock.close()
The above is the detailed content of Tutorial on implementing FTP using socket. For more information, please follow other related articles on the PHP Chinese website!