python學習筆記_week8
一、Socket
當服務端傳的東西大于用戶端的最大值時怎麼辦?①改大buffer不行(有上限)②多傳幾次(用for循環必須要知道循環幾次,是以不用for循環,用while)
服務端:
1 import os
2 import socket
3 server=socket.socket()
4 server.bind(("localhost",9999))
5
6 server.listen()
7
8 while True:
9 conn,addr=server.accept()
10 print("new conn:",addr)
11 while True:
12 print("等待新指令")
13 data=conn.recv(700)
14 if not data :
15 print("用戶端已斷開")
16 break
17 print("執行指令:",data)
18 cmd_res=os.popen(data.decode()).read() #接受字元串,執行結果也是字元串
19 print("before send",len(cmd_res.encode()))
20 if len(cmd_res)==0:
21 cmd_res="cmd has no output..."
22 conn.send(str(len(cmd_res.encode())).encode("utf-8")) #先發大小給用戶端
23 #字元串才能encode,cmd_res要encode 否則因為中文的原因長度會不相等
24 conn.send(cmd_res.encode("utf-8"))
25 print("send done")
26 server.close()
View Code
用戶端:
1 import socket
2 client=socket.socket()
3 client.connect(("localhost",9999))
4
5 while True:
6 cmd=input(">>:").strip()
7 if len(cmd) == 0 :
8 continue
9 client.send(cmd.encode("utf-8"))
10 cmd_res_size=client.recv(700) #接收命定結果的長度
11 print("指令結果:",cmd_res_size)
12 received_size=0
13 received_data=b""
14 while received_size != int(cmd_res_size.decode()):
15 data=client.recv(700)
16 received_size+=len(data) #每次收到的有可能小于700,是以必須用len判斷
17 # print(data.decode())
18 received_data+=data
19 else:
20 print("cmd res receive done",received_size)
21 # cmd_res=client.recv(700)
22 print(received_data.decode())
23 client.close()
View Code
socket粘包:兩次send緊挨着,緩沖區将其打包成一次send,導緻出錯(用time.sleep()解決太low了,不要用),可以在兩次send間插入一次互動,如在伺服器端client_ack=conn.recv(700) #wait client to confirm,在用戶端client.send("準備好接收了,loser可以發了".encode("utf-8")) PS:windows上粘包現象可能不會顯示出來,但Linux一定會。
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIn5GcuQTN1UzMwUDMx0CN1YTM1YzMxIzMwETM3EDMy0CMyMTO0ITMvwVMxcTMwIzLcBjMzkDNyEzLcd2bsJ2Lc12bj5ycn9Gbi52YucTMwIzcldWYtl2Lc9CX6MHc0RHaiojIsJye.png)
ftp server:1.讀取檔案名;2.檢測檔案是否存在;3.打開檔案;4.檢測檔案大小;5.發送檔案大小給用戶端;6.等用戶端确認;7.開始邊讀邊發資料;8.發送md5;
服務端:
1 import hashlib
2 import os
3 import socket
4 server=socket.socket()
5 server.bind(("localhost",9999))
6
7 server.listen()
8
9 while True:
10 conn,addr=server.accept()
11 print("new conn:",addr)
12 while True:
13 print("等待新指令")
14 data=conn.recv(700)
15 if not data :
16 print("用戶端已斷開")
17 break
18 cmd,filename=data.decode().split()
19 print(filename)
20 if os.path.isfile(filename): #判斷檔案是否存在
21 f=open(filename,"rb")
22 m=hashlib.md5()
23 file_size=os.stat(filename).st_size #檔案大小
24 conn.send(str(file_size).encode()) #send file size
25 conn.recv(700) #wait for ack
26 for line in f:
27 m.update(line)
28 conn.send(line)
29 print("file md5",m.hexdigest()) #加上md5速度會慢下來
30 f.close()
31 conn.send(m.hexdigest().encode()) #send md5
32 print("send done")
33 server.close()
View Code
用戶端:
1 import socket
2 import hashlib
3 client=socket.socket()
4 client.connect(("localhost",9999))
5
6 while True:
7 cmd=input(">>:").strip()
8 if len(cmd) == 0 :
9 continue
10 if cmd.startswith("get"):
11 client.send(cmd.encode())
12 server_response=client.recv(700)
13 print("server response",server_response)
14 client.send(b"ready to recv file")
15 file_total_size=int(server_response.decode())
16 received_size=0
17 filename=cmd.split()[1]
18 f=open(filename+".new","wb")
19 m=hashlib.md5()
20 while received_size < file_total_size:
21 if file_total_size - received_size > 700:#要收不止一次
22 size=700
23 else: #最後一次了,剩多少就隻收多少
24 size=file_total_size-received_size
25 #上面的判斷是為了防止粘包。粘包隻可能發生在最後一次
26 print("last receive:",size)
27 data=client.recv(size)
28 received_size+=len(data)
29 m.update(data)
30 f.write(data)
31 #print(file_total_size,received_size)
32 else:
33 new_file_md5=m.hexdigest()
34 print("file recv done",file_total_size,received_size)
35 f.close()
36 server_file_md5=client.recv(700)
37 print("server file md5:",server_file_md5)
38 print("client file md5:",new_file_md5)
39 client.close()
View Code
二、Socketsever
最主要的作用:并發處理。定義:簡化網絡任務伺服器端的編寫(對socket的再封裝)
1.
socketserver.
TCPServer;2.
socketserver.
UDPServer;3.
socketserver.
UnixStreamServer;4.
socketserver.
UnixDatagramServer
建立SocketServer的步驟:
1.你必須自己建立一個請求處理類,并且這個類要繼承BaseRequestHandler,并且還要重寫父類裡的handle()。
2.你必須執行個體化TCPserver(其他也行),并且傳遞server ip 和你上面建立的請求處理類給這個TCPserver。
3.server.handle_request() #隻處理一個請求(不常用)
server.handle_forever()#處理多個請求,永遠執行
調用server_close()來關閉
跟用戶端所有的互動都是在handle裡完成的
1 import socketserver
2
3 class MyTCPHandler(socketserver.BaseRequestHandler):
4 """
5 The request handler class for our server.
6
7 It is instantiated once per connection to the server, and must
8 override the handle() method to implement communication to the
9 client.
10 """
11
12 def handle(self):
13 while True:
14 try:
15 # self.request is the TCP socket connected to the client
16 self.data = self.request.recv(1024).strip() #每一個用戶端的請求過來都會執行個體化MyTCPHandler
17 print("{} wrote:".format(self.client_address[0]))
18 print(self.data)
19 # if not self.data:#用戶端斷了
20 # print(self.client_address,"斷開了")
21 # break
22 # just send back the same data, but upper-cased
23 self.request.send(self.data.upper())
24 except ConnectionResetError as e:
25 print("err:",e)
26 break
27
28 if __name__ == "__main__":
29 HOST, PORT = "localhost", 9999
30
31 # Create the server, binding to localhost on port 9999
32 server = socketserver.TCPServer((HOST, PORT), MyTCPHandler) #把ip位址和類當做參數傳給TCPServer,TCPServer就開始監聽
33 # 和執行個體化MyTCPHandler,拿handle()與用戶端互動
34
35 # Activate the server; this will keep running until you
36 # interrupt the program with Ctrl-C
37 server.serve_forever()
View Code
多并發:每來一個請求,開啟一個新線程。
讓你的socketserver并發起來, 必須選擇使用以下一個多并發的類
class
socketserver.
ForkingTCPServer
class
socketserver.
ForkingUDPServer
class
socketserver.
ThreadingTCPServer
class
socketserver.
ThreadingUDPServer
ForkingTCPServer 多程序(效果與多線程一樣)。(在windows上不能執行,沒有fork)
socketserver.
BaseServer
(server_address, RequestHandlerClass) 主要有以下方法:1.fileno() 傳回檔案描述符(一般用不到)2.handle_request() 處理單個請求
3.serve_forever(poll_interval=0.5) 每0.5秒檢測一下是否有shutdown信号 4.service_actions() 5.shutdown() 6.server_close() 7.address_family 8.RequestHandlerClass 9.server_address 10.socket 11.allow_reuse_address 12.request_queue_size 13.socket_type 14.timeout 15.finish_request()(self.setup(),self.handle(),self.finish())16.get_request() 17.handle_error(request, client_address) 18.handle_timeout() 19.process_request(request, client_address) 20.server_activate() 21.server_bind() 22.verify_request(request, client_address)
作業:開發一個支援多使用者線上的FTP程式
要求:
- 使用者加密認證
- 允許同時多使用者登入
- 每個使用者有自己的家目錄 ,且隻能通路自己的家目錄
- 對使用者進行磁盤配額,每個使用者的可用空間不同
- 允許使用者在ftp server上随意切換目錄
- 允許使用者檢視目前目錄下檔案
- 允許上傳和下載下傳檔案,保證檔案一緻性
- 檔案傳輸過程中顯示進度條
- 附加功能:支援檔案的斷點續傳
伺服器端:
1 import socketserver
2 import json,os
3 class MyTCPHandler(socketserver.BaseRequestHandler):
4 def put(self,*args):
5 '''接受用戶端檔案'''
6 cmd_dic=args[0]
7 filename=cmd_dic["filename"]
8 file_size=cmd_dic["size"]
9 if os.path.isfile(filename):
10 f=open(filename+".new","wb")
11 else:
12 f=open(filename,"wb")
13 self.request.send(b"200 ok") #傳回用戶端請求
14 receive_size=0
15 while receive_size<file_size:
16 data=self.request.recv(1024)
17 f.write(data)
18 receive_size+=len(data)
19 else:
20 print("file [%s] has uploaded..."%filename)
21 def handle(self):
22 while True:
23 try:
24 self.data = self.request.recv(1024).strip() #每一個用戶端的請求過來都會執行個體化MyTCPHandler
25 print("{} wrote:".format(self.client_address[0]))
26 print(self.data)
27 cmd_dic=json.loads(self.data.decode())
28 action=cmd_dic["action"]
29 if hasattr(self,action):
30 func=getattr(self,action)
31 func(cmd_dic)
32 self.request.send(self.data.upper())
33 except ConnectionResetError as e:
34 print("err:",e)
35 break
36 if __name__ == "__main__":
37 HOST, PORT = "localhost", 9999
38 server = socketserver.ThreadingTCPServer((HOST, PORT), MyTCPHandler) #把ip位址和類當做參數傳給TCPServer,TCPServer就開始監聽
39 server.serve_forever()
View Code
用戶端:
1 import socket
2 import json
3 import os
4 class Ftpclient(object):
5 def __init__(self):
6 self.client = socket.socket()
7 def help(self):
8 msg='''
9 ls
10 pwd
11 cd../..
12 get filename
13 put filename'''
14 print(msg)
15 def connect(self,ip,port):
16 self.client.connect((ip, port))
17 def interactive(self):
18 #self.authenticate() #使用者登入
19 while True:
20 cmd=input(">>:")
21 if len(cmd)==0:continue
22 cmd_str=cmd.split()[0]#第一個值是指令
23 if hasattr(self,"cmd_%s"%cmd_str):
24 func=getattr(self,"cmd_%s"%cmd_str)
25 func(cmd)
26 else:
27 self.help()
28 def cmd_put(self,*args):
29 cmd_split=args[0].split()
30 if len(cmd_split) > 1:
31 filename=cmd_split[1]
32 if os.path.isfile(filename):
33 file_size=os.stat(filename).st_size
34 # msg_str="%s|%s"%(filename,file_size) #寫死了,要考慮長遠
35 msg_dic={
36 "action":"put",
37 "filename":filename,
38 "size":file_size,
39 "overriden":True
40 }
41 self.client.send(json.dumps(msg_dic).encode("utf-8"))
42 print("send",json.dumps(msg_dic).encode("utf-8"))
43 #防止粘包,等伺服器确認
44 server_response=self.client.recv(1024)
45 f=open(filename,"rb")
46 for line in f:
47 self.client.send(line)
48 else:
49 print("file upload success...")
50 f.close()
51 else:
52 print(filename,"is not exist")
53 def cmd_get(self):
54 pass
55 ftp=Ftpclient()
56 ftp.connect("localhost",9999)
57 ftp.interactive()
View Code
posted on 2017-11-03 19:58 我很好u 閱讀( ...) 評論( ...) 編輯 收藏
轉載于:https://www.cnblogs.com/jyh-py-blog/p/7780246.html