1.线程队列
线程队列有三种:先进先出,后进先出,按优先级进出,具体如下:
1 importqueue2
3 #先进先出
4 q = queue.Queue(3)5
6 q.put(1)7 q.put(2)8 q.put(3)9 #q.put(4) # 再放阻塞,等待队列消费
10 #q.put(4,block = False) # 不阻塞,强制放数据,如果满的情况下直接报错 等价与 q.put_nowait(4)
11 #q.put(4,block = True) # 阻塞,等待放数据,如果满的情况下阻塞,默认是True
12 #q.put(4, block=True, timeout=3) # 阻塞等待3秒,3秒还在阻塞,强制放数据,满的情况下报错
13 print(q.full())14 print(q.empty())15
16 print(q.get())17 print(q.get())18 print(q.get())19 #print(q.get()) # 再拿阻塞,等待队列新增数据 block timeout同put
20 print(q.full())21 print(q.empty())22
23
24 #后进先出 同堆栈原理
25 q = queue.LifoQueue(3)26
27 q.put(1)28 q.put(2)29 q.put(3)30 #q.put(4) # 再放阻塞,等待队列消费
31 #q.put(4,block = False) # 不阻塞,强制放数据,如果满的情况下直接报错 等价与 q.put_nowait(4)
32 #q.put(4,block = True) # 阻塞,等待放数据,如果满的情况下阻塞,默认是True
33 #q.put(4, block=True, timeout=3) # 阻塞等待3秒,3秒还在阻塞,强制放数据,满的情况下报错
34 print(q.full())35 print(q.empty())36
37 print(q.get())38 print(q.get())39 print(q.get())40 #print(q.get()) # 再拿阻塞,等待队列新增数据 block timeout同put
41 print(q.full())42 print(q.empty())43
44 #优先级进出 优先级越小的先出
45 q = queue.PriorityQueue(3)46
47 q.put([50, 1])48 q.put([20, 2])49 q.put([30, 3])50 #q.put([50, 4]) # 再放阻塞,等待队列消费
51 print(q.full())52 print(q.empty())53
54 print(q.get())55 print(q.get())56 print(q.get())57 #print(q.get()) # 再拿阻塞,等待队列新增数据 block timeout同put
58 print(q.full())59 print(q.empty())
View Code
2.进程池&线程池
在刚开始学多进程或多线程时,我们迫不及待地基于多进程或多线程实现并发的套接字通信。
然而这种实现方式的致命缺陷是:服务的开启的进程数或线程数都会随着并发的客户端数目地增多而增多,这会对服务端主机带来巨大的压力,甚至于不堪重负而瘫痪。
于是我们必须对服务端开启的进程数或线程数加以控制,让机器在一个自己可以承受的范围内运行,这就是进程池或线程池的用途。
例如进程池,就是用来存放进程的池子,本质还是基于多进程,只不过是对开启进程的数目加上了限制。
2.1基本用法:
1、submit(fn, *args, **kwargs)
异步提交任务
2、map(func, *iterables, timeout=None, chunksize=1)
取代for循环submit的操作
3、shutdown(wait=True)
相当于进程池的pool.close()+pool.join()操作
wait=True,等待池内所有任务执行完毕回收完资源后才继续
wait=False,立即返回,并不会等待池内的任务执行完毕
但不管wait参数为何值,整个程序都会等到所有任务执行完毕
submit和map必须在shutdown之前
4、result(timeout=None)
取得结果
5、add_done_callback(fn)
回调函数
1 from concurrent.futures importProcessPoolExecutor, ThreadPoolExecutor2 from threading importcurrent_thread3 importtime, random, os4
5
6 defsayhi(name):7 print("%s say hi... pid:%s; current_thread:%s" %(name, os.getpid(), current_thread().getName()))8 time.sleep(random.randint(1, 3))9 print("%s say bye... pid:%s; current_thread:%s" %(name, os.getpid(), current_thread().getName()))10
11
12 if __name__ == "__main__":13 #pool = ProcessPoolExecutor(3) # 实例化进程池,指定最大进程数为3
14 pool = ThreadPoolExecutor(3) #实例化线程池,指定最大线程数为3
15 for i in range(10):16 pool.submit(sayhi, "xg%s" %i,)17 #关闭pool的submit功能,不可以再丢进程或线程进线程池。
18 pool.shutdown(wait=True) #此刻统计当前pool里的所有进程或线程数,每运行完一个-1,直到等于0时,往下运行代码。等同于进程线程的join
19 print("all over!")
View Code
2.2同步回调 开启的多线程变成了串行,拿到第一个线程的执行结果才继续往下继续运行
1 #钓鱼大赛,参赛者钓鱼,然后称重。
2 from concurrent.futures importThreadPoolExecutor3 importtime, random, os4
5
6 deffishing(name):7 print("%s is fishing..." %name)8 time.sleep(random.randint(2, 5))9 fish = random.randint(5, 15) * "m"
10 res = {"name": name, "fish": fish}11 returnres12
13
14 defweigh(res):15 name = res["name"]16 size = len(res["fish"])17 print("%s 钓到的鱼大小为 %s kg" %(name, size))18
19
20 if __name__ == "__main__":21 pool = ThreadPoolExecutor(3)22 res1 = pool.submit(fishing, "xt").result() #同步拿结果,拿到结果才继续往下走
23 weigh(res1)24 res2 = pool.submit(fishing, "dj").result()25 weigh(res2)26 res3 = pool.submit(fishing, "hh").result()27 weigh(res3)
View Code
2.3异步回调
1 from concurrent.futures importThreadPoolExecutor2 importtime, random, os3
4
5 deffishing(name):6 print("%s is fishing..." %name)7 time.sleep(random.randint(2, 5))8 fish = random.randint(5, 15) * "m"
9 res = {"name": name, "fish": fish}10 returnres11
12
13 defweigh(pool_obj):14 res = pool_obj.result() #拿到线程对象的运行结果,因为是线程运行完才会调用weigh,所以马上能拿到结果
15 name = res["name"]16 size = len(res["fish"])17 print("%s 钓到的鱼大小为 %s kg" %(name, size))18
19
20 if __name__ == "__main__":21 pool = ThreadPoolExecutor(3)22 pool.submit(fishing, "xt").add_done_callback(weigh) #当线程执行完后,将线程对象当参数传给weigh
23 pool.submit(fishing, "dj").add_done_callback(weigh)24 pool.submit(fishing, "hh").add_done_callback(weigh)
View Code
2.4map用法
1 from concurrent.futures importThreadPoolExecutor,ProcessPoolExecutor2
3 importos,time,random4 deftask(n):5 print('%s is runing' %os.getpid())6 time.sleep(random.randint(1,3))7 return n**2
8
9 if __name__ == '__main__':10
11 executor=ThreadPoolExecutor(max_workers=3)12
13 #for i in range(11):
14 #future=executor.submit(task,i)
15
16 executor.map(task,range(1,12)) #map取代了for+submit
View Code
3.queue实现的线程池
套接字服务端代码:
1 importsocket, queue2 from threading importThread, currentThread3
4
5 server = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)6 server.bind(("127.0.0.1", 8089))7 server.listen(1000)8 #pool = ThreadPoolExecutor(2)
9 q = queue.Queue(1000)10
11
12 defrec_data():13 whileTrue:14 conn =q.get()15 whileTrue:16 try:17 res = conn.recv(1024)18 if not res:break
19 res =res.upper()20 conn.send(res)21 print("server cunrrent thread: %s" %currentThread().getName())22 exceptException as e:23 print(e)24 conn.close()25 q.task_done()26 break
27
28
29 defstart():30 print("starting...")31 for i in range(2):32 t = Thread(target=rec_data)33 t.daemon =True34 t.start()35 whileTrue:36 conn, addr =server.accept()37 q.put(conn)38 #pool.submit(rec_data, conn, addr)
39
40
41 if __name__ == "__main__":42 start()
View Code
上面的配套客户端代码:
1 importsocket2 from threading importThread, currentThread3
4
5 defsend_msg():6 client = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)7 client.connect(("127.0.0.1", 8089))8 count = 1
9 whileTrue:10 msg = input(">>>>:")11 #msg = "%s say hello %s " % (currentThread().getName(), count)
12 print("%s trying to send datas..." %currentThread().getName())13 client.send(msg.encode())14 print("%s trying to resv datas..." %currentThread().getName())15 res = client.recv(1024)16 count += 1
17 print(res)18
19
20 if __name__ == "__main__":21 #for i in range(100):
22 #t = Thread(target=send_msg)
23 #t.start()
24 send_msg()
View Code
4.基于线程的FTP服务器
1.在之前开发的FTP基础上,开发支持多并发的功能
2.不能使用SocketServer模块,必须自己实现多线程
3.必须用到队列Queue模块,实现线程池
4.允许配置最大并发数,比如允许只有10个并发用户
实现功能如下:
用户加密认证
允许同时多用户登录(用到并发编程的知识,选做)
每个用户有自己的家目录,且只能访问自己的家目录
对用户进行磁盘配额,每个用户的可用空间不同(选做)
允许用户在ftp server上随意切换目录
允许用户查看当前目录下的文件
允许上传和下载文件,并保证文件的一致性
文件传输过程中显示进度条
服务端代码:
1 #-*- coding: utf-8 -*-
2 #@Time : 2018/12/14 9:11
3 #@Author : Xiao
4
5
6 importsocket7 importos8 importsys9 importstruct10 importjson11 importhashlib12 importconfigparser13 importsubprocess14 importqueue15 from threading importThread16 from conf.settings import *
17
18
19 classMyserver(object):20 server_socket = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)21 server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)22 server_socket.bind((SERVER_IP, SERVER_PORT))23 server_socket.listen(MAX_CONNECT)24 q = queue.Queue(MAX_QUEUE) #初始化队列大小,最对只能有多少个等待链接
25
26 def __init__(self, conn):27 """实例化时自动启动文件服务"""
28 self.conn = conn #接收到的客户连接信息
29 self.client_addr = None #接收到的客户连接地址信息
30 self.header_len_bytes = None #发送数据的报文头信息
31 self.users = self.get_users() #拿到用户信息,登录的时候用来判断
32 self.online = 0 #用户的登录状态,每次接收到客户端的命令都要判断用户是否已经登录,未登录需要先登录
33 self.home = None #用户的家目录,用户登录的时候会更新家目录
34 self.cur = None #用户的当前目录,用户登录的时候当前目录就是家目录,随着用户cd命令执行更新
35 self.quota = 0 #用户的家目录的大小限制,用户上传时会先判断目录大小是否够接收文件
36 print("starting....")37
38 @staticmethod39 defget_md5(var):40 """"加密,盐值为123456"""
41 salt = "123456"
42 new_var = salt +var43 m =hashlib.md5()44 m.update(new_var.encode())45 returnm.hexdigest()46
47 @staticmethod48 defget_users():49 '''拿到用户基础信息'''
50 """"初始化用户信息"""
51 users =configparser.ConfigParser()52 users.read(DATA_PATH)53 returnusers54
55 @property56 defget_code(self):57 """拿到客户端上传文件的操作结果"""
58 code_len_bytes = self.conn.recv(4)59 code_len = struct.unpack("i", code_len_bytes)[0] #struct.unpack解压数据,得到数据头信息长度
60 code_str = self.conn.recv(code_len).decode("utf-8") #根据上面的长度接收数据头信息
61 code_dic = json.loads(code_str, encoding="utf-8")62 code = code_dic["code"]63 msg = code_dic["msg"]64 returncode, msg65
66 @property67 defget_msg(self):68 """拿到客户端发来的请求"""
69 header_len = struct.unpack("i", self.header_len_bytes)[0] #struct.unpack解压数据,得到数据头信息长度
70 header_str = self.conn.recv(header_len).decode("utf-8") #根据上面的长度接收数据头信息
71 header = json.loads(header_str, encoding="utf-8")72 msg_size = header["msg_size"] #根据数据头信息得到本次要接收的数据大小
73 msg = self.conn.recv(msg_size).decode("utf-8")74 returnmsg75
76 defsend_code(self,code_dic):77 """发送本次请求的结果状态,客户端根据这些状态做下一步操作"""
78 res_code_bytes = bytes(json.dumps(code_dic), encoding='utf-8')79 res_code_len_bytes = struct.pack("i", len(res_code_bytes))80 self.conn.send(res_code_len_bytes)81 self.conn.send(res_code_bytes)82
83 def_login(self, user_info):84 """登陆逻辑,登录成功后初始化用户的家目录、当前目录、登录状态等信息"""
85 info_lis = user_info.split(",")86 if len(info_lis) == 2:87 user, pwd =info_lis88 pwd =self.get_md5(pwd)89 if user in self.users and pwd == self.users[user].get("password"):90 code_dic = {"code": "0", "msg": "login success!"}91 self.send_code(code_dic)92 self.home = os.path.join(HOME_PATH, self.users[user].get("home_dir"))93 self.cur =self.home94 self.quota = float(self.users[user].get("quota"))*(1024**3)95 self.online = 1
96 returnTrue97 code_dic = {"code": "1", "msg": "error username or password!"}98 self.send_code(code_dic)99
100 def_sz(self, file_name):101 """发送文件给客户端102 1.首先拿到客户端的确认信息,是否上传103 2.确定后,判断文件是否存在,并告诉客户端接下来是传文件还是通知文件不存在104 3.文件存在则开始发送文件105 """
106 res_code, res_msg =self.get_code107 if res_code == "0":108 file_path =os.path.join(self.cur, file_name)109 if os.path.exists(file_path) andos.path.isfile(file_path):110 code_dic = {"code": "0", "msg": "start to download %s" %file_name}111 self.send_code(code_dic)112 file_size =os.path.getsize(file_path)113 header = {"file_size": file_size, "file_name": file_name, "md5": "123456"}114 header_bytes = bytes(json.dumps(header), encoding='utf-8')115 header_len_bytes = struct.pack("i", len(header_bytes))116 self.conn.send(header_len_bytes)117 self.conn.send(header_bytes)118 with open(file_path, "rb") as f:119 for line inf:120 self.conn.send(line)121 else:122 code_dic = {"code": "1", "msg": "%s is a directory or file doesn't exist!" %file_name}123 self.send_code(code_dic)124
125 def_rz(self, file_name):126 """保存来自文件127 1.首先确认客户端是否要传文件128 2.确定后,判断文件是否存在,并告诉客户端接下来是接收文件还是通知文件已存在129 3.用户当前目录文件不存在则开始接收文件130 """
131 res_code, res_msg =self.get_code132 if res_code == "0":133 file_name =os.path.basename(file_name)134 file_abspath =os.path.join(self.cur, file_name)135 if notos.path.exists(file_abspath):136 res_code = {"code": "0", "msg": "start to upload file %s..." %file_name}137 self.send_code(res_code)138 header_len_bytes = self.conn.recv(4) #接收4个字节的数据头信息
139 header_len = struct.unpack("i", header_len_bytes)[0] #struct.unpack解压数据,得到数据头信息长度
140 header_str = self.conn.recv(header_len).decode("utf-8") #根据上面的长度接收数据头信息
141 header = json.loads(header_str, encoding="utf-8")142 file_size = header["file_size"] #根据数据头信息得到本次要接收的数据大小
143 empty_size =float(self.quota) -os.path.getsize(self.home)144 if empty_size <145 res_code='{"code":' space left to accept file self.send_code else:148 recv_size="0151" with open as f:152 while file_size:>
153 line = self.conn.recv(1024)154 f.write(line) #将每次接收到的数据拼接
155 recv_size += len(line) #实时记录当前接收到的数据长度
156 else:157 res_code = {"code": "1", "msg": "%s is already exists..." %file_name}158 self.send_code(res_code)159
160 def_ls(self, dirname):161 """
162 1.接收客户端需要查看的是哪个目录163 2.判断目录是否存在,存在继续往下走,不存在则直接告诉客户端失败,目录不存在164 3.执行命令,如果服务器端是linux系统,则用ls,如果是windows则用dir165 4.如果命令执行结果为空,返回客户端当前目录下没有文件166 5.如果不为空,则开始发送目录下的文件夹或文件信息167 """
168 new_dirname = os.path.join(self.cur, dirname) if dirname != "." elseself.cur169 print(new_dirname)170 cmd = "dir" if sys.platform.lower().startswith("win") else "ls"
171 if os.path.exists(new_dirname) and notos.path.isfile(new_dirname):172 res = subprocess.Popen("%s %s" % (cmd, new_dirname), shell=True, stdout=subprocess.PIPE)173 out =res.stdout.read()174 ifout:175 print(out.decode("GBK"))176 res_code = {"code": "0", "msg": "%s dir has follow files or dirs..." %dirname}177 self.send_code(res_code)178 header = {"file_size": len(out)}179 header_bytes = bytes(json.dumps(header), encoding='utf-8')180 header_len_bytes = struct.pack("i", len(header_bytes))181 self.conn.send(header_len_bytes)182 self.conn.send(header_bytes)183 self.conn.send(out)184 else:185 res_code = {"code": "3", "msg": "%s current dir is empty..." %dirname}186 self.send_code(res_code)187 else:188 res_code = {"code": "1", "msg": "%s no such directory" %dirname}189 self.send_code(res_code)190
191 def_cd(self, dirname):192 """
193 1.接收到客户端的需要进入的目录信息,跟当前目录进行拼接194 2.如果目录存在,则修改当前目录的变量值,如果不存在则告诉客户端,目录不存在195 """
196 new_dirname =os.path.join(self.cur, dirname)197 if os.path.exists(new_dirname) and notos.path.isfile(new_dirname):198 res_code = {"code": "0", "msg": "切换成功,当前目录为 %s" %dirname}199 self.send_code(res_code)200 self.cur =new_dirname201 else:202 res_code = {"code": "1", "msg": "切换失败, %s 目录不存在" %dirname}203 self.send_code(res_code)204
205 defcomunication(self):206 """
207 通信主程序,每个实例都是通过这个方法和客户端通信的。208 1.先判断用户的是否登陆,如果没有登陆而且请求不是login,则返回客户端让其登陆,如果已登陆则往下走209 2.判断用户请求的方法是否正确,不正确则返回客户端,请求方法有误,如果方法存在则往下走210 3.调用具体的方法211 """
212 whileTrue:213 try:214 self.header_len_bytes = self.conn.recv(4) #接收4个字节的数据头信息
215 if notself.header_len_bytes:216 break
217 msg =self.get_msg218 print(msg)219 method, args = msg.split(" ", 1)220 if not self.online and method != "login":221 res_code = {"code": "2", "msg": "please login first!"}222 self.send_code(res_code)223 elif hasattr(self, "_%s" % method.lower()) andargs:224 res_code = {"code": "0", "msg": "wait moment,it's working now"}225 self.send_code(res_code)226 func = getattr(self, "_%s" %method.lower())227 func(args)228 else:229 res_code = {"code": "1", "msg": "error request %s !" %msg}230 self.send_code(res_code)231 exceptException as e:232 print(e)233 self.q.task_done()234 self.conn.close()235 break
236
237 @classmethod238 defstart(cls):239 """
240 循环拿队列q里的链接,拿到一个实例化一个,然后启动comunication方法,开始和客户端交互241 """
242 whileTrue:243 conn =cls.q.get()244 client =Myserver(conn)245 client.comunication()246
247 @classmethod248 defcreate_thread(cls):249 '''
250 开启多线程,线程数为settings设置的最大并发数251 '''
252 for i inrange(MAX_RUN):253 t = Thread(target=cls.start)254 t.daemon =True255 t.start()256
257 @classmethod258 defrun(self):259 """
260 启动,循环等链接,每来一个链接就赛到队列里261 """
262 self.create_thread()263 whileTrue:264 print("waiting for connection...")265 conn, client_addr =self.server_socket.accept()266 self.q.put(conn)267
268
269 if __name__ == "__main__":270 Myserver.run()
View Code
客户端代码:
1 #-*- coding: utf-8 -*-
2 #@Time : 2018/12/14 9:11
3 #@Author : Xiao
4
5
6 importsocket7 importstruct8 importjson9 importos10 from settings import *
11
12
13 classMyclient(object):14 def __init__(self):15 self.server_socket = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)16 self.server_socket.connect((SERVER_IP, SERVER_PORT)) #主动初始化TCP服务器连接
17
18 defsend_msg(self, msg_bytes):19 """发送本次请求的指令,服务端根据指令返回数据"""
20 header = {"msg_size": len(msg_bytes)}21 header_bytes = json.dumps(header).encode("utf-8")22 header_size = struct.pack("i", len(header_bytes))23 self.server_socket.send(header_size)24 self.server_socket.send(header_bytes)25 self.server_socket.send(msg_bytes)26
27 defsend_code(self, code_dic):28 """发送上传文件的结果状态,服务端根据这些状态做下一步操作"""
29 res_code_bytes = bytes(json.dumps(code_dic), encoding='utf-8')30 res_code_len_bytes = struct.pack("i", len(res_code_bytes))31 self.server_socket.send(res_code_len_bytes)32 self.server_socket.send(res_code_bytes)33
34 @property35 defget_code(self):36 """拿到服务端关于本次指令的操作结果"""
37 code_len_bytes = self.server_socket.recv(4)38 code_len = struct.unpack("i", code_len_bytes)[0] #struct.unpack解压数据,得到数据头信息长度
39 code_str = self.server_socket.recv(code_len).decode("utf-8") #根据上面的长度接收数据头信息
40 code_dic = json.loads(code_str, encoding="utf-8")41 code = code_dic["code"]42 msg = code_dic["msg"]43 returncode, msg44
45 deflogin(self):46 """登陆函数"""
47 count =048 while count < 3:49 user = input("your name:").strip()50 password = input("your password:").strip()51 if user andpassword:52 msg = "login %s,%s" %(user, password)53 msg_bytes = msg.encode("utf-8")54 self.send_msg(msg_bytes)55 res_code, res_msg =self.get_code56 print(res_msg)57 if res_code == "0":58 login_code, login_msg =self.get_code59 print(login_msg)60 if login_code == "0":61 Myclient.online = 1
62 break
63 count += 1
64 else:65 print("账号或密码不能为空!")66 else:67 exit("too many login!")68
69 def_sz(self, file_name):70 """下载文件71 1.首先判断客户端本地是否存在文件,存在则告诉服务端不用传了,不存在则往下走72 2.接收服务器端返回的文件操作结果,是否可以下载,不可以现在则打印服务端的msg,可以下载则开始接收文件73 """
74 file_abspath =os.path.join(DOWNLOAD_PATH, file_name)75 if notos.path.exists(file_abspath):76 data_code = {"code": "0", "msg": file_name}77 self.send_code(data_code)78 res_code, res_msg =self.get_code79 print(res_msg)80 if res_code == "0":81 header_len_bytes = self.server_socket.recv(4) #接收4个字节的数据头信息
82 header_len = struct.unpack("i", header_len_bytes)[0] #struct.unpack解压数据,得到数据头信息长度
83 header_str = self.server_socket.recv(header_len).decode("utf-8") #根据上面的长度接收数据头信息
84 header = json.loads(header_str, encoding="utf-8")85 file_size = header["file_size"] #根据数据头信息得到本次要接收的数据大小
86 recv_size =087 tmp_size =088 per_size = file_size/50
89 num = 1
90 with open(file_abspath, "wb") as f:91 while recv_size < file_size: #当接收到的数据小于本次数据长度时就一直接收
92 if tmp_size >per_size:93 rate = str((recv_size / file_size)*100)[:5] + "%"
94 print("%s %s" % ("#"*num, rate))95 tmp_size =096 num += 1
97 line = self.server_socket.recv(1024)98 f.write(line) #将每次接收到的数据拼接
99 recv_size += len(line) #实时记录当前接收到的数据长度
100 tmp_size +=len(line)101 print("%s %s" % ("#" * (num+1), "100.00%"))102 print("download file %s success!" %file_name)103 else:104 res_code = {"code": "1", "msg": file_name}105 self.send_code(res_code)106 print("%s is already exists" %file_name)107
108 def_rz(self, file_path):109 """上传文件110 1.首先判断客户端本地是否存在文件,不存在则告诉服务端不传了,存在则往下走111 2.接收服务器端返回的文件操作结果,是否可以上传,不可以现在则打印服务端的msg,可以上传则开始发送文件112 """
113 if os.path.exists(file_path) andos.path.isfile(file_path):114 file_name =os.path.basename(file_path)115 res_code = {"code": "0", "msg": file_name}116 self.send_code(res_code)117 res_code, res_msg =self.get_code118 print(res_msg)119 if res_code == "0":120 file_size =os.path.getsize(file_path)121 header = {"file_size": file_size, "file_name": file_name, "md5": "123456"}122 header_bytes = bytes(json.dumps(header), encoding='utf-8')123 header_len_bytes = struct.pack("i", len(header_bytes))124 self.server_socket.send(header_len_bytes)125 self.server_socket.send(header_bytes)126 res_code, res_msg =self.get_code127 print(res_msg)128 upload_size =0129 tmp_size =0130 per_size = file_size / 50
131 num = 1
132 if res_code == "0":133 with open(file_path, "rb") as f:134 for line inf:135 if tmp_size >per_size:136 rate = str((upload_size / file_size) * 100)[:5] + "%"
137 print("%s %s" % ("#" *num, rate))138 tmp_size =0139 num += 1
140 self.server_socket.send(line)141 upload_size +=len(line)142 tmp_size +=len(line)143 print("%s %s" % ("#" * (num + 1), "100.00%"))144 print("upload file %s success!" %file_name)145 else:146 res_code = {"code": "1", "msg": "no such file: %s" %file_path}147 self.send_code(res_code)148 print("上传文件不存在!")149
150 def_ls(self, dirname):151 """查看当前文件夹下的文件或目录!"""
152 res_code, res_msg =self.get_code153 print(res_msg)154 if res_code == "0":155 header_len_bytes = self.server_socket.recv(4)156 header_len = struct.unpack("i", header_len_bytes)[0]157 header_str = self.server_socket.recv(header_len).decode("utf-8")158 header = json.loads(header_str, encoding="utf-8")159 file_size = header["file_size"]160 recv_size =0161 res = b''
162 while recv_size <163 res self.server_socket.recv recv_size="len(res)165" print>
168 def_cd(self, dirname):169 """切换目录"""
170 res_code, res_msg =self.get_code171 print(res_msg)172
173 defrun(self):174 """反复向服务器发送请求,当请求的返回操作码为0成功时,调用相应属性,失败时不做任何处理直接循环"""
175 self.login()176 whileTrue:177 msg = input(">>>>:").strip()178 if msg.lower() == "ls":179 msg = msg + "."
180 msg_lis = msg.split(" ", 1)181 if len(msg_lis) == 2 and hasattr(self, "_%s" %msg_lis[0].lower()):182 method =msg_lis[0].lower()183 args = msg_lis[1]184 msg_bytes = msg.encode("utf-8")185 self.send_msg(msg_bytes)186 res_code, res_msg =self.get_code187 print(res_msg)188 if res_code == "2":189 self.login()190 if res_code == "0":191 func = getattr(self, "_%s" %method.lower())192 func(args)193 else:194 print("输入格式不正确,请重新输入!")195
196 def __del__(self):197 '''析构函数,当程序结束时自动关闭socket'''
198 self.server_socket.close()199
200
201 if __name__ == "__main__":202 try:203 client =Myclient()204 client.run()205 exceptException as e:206 print(e)
View Code
163>145>